Checking merge against latest trunk
authornemo
Thu, 30 Aug 2012 13:02:19 -0400
changeset 7628 bc7b1d228a2c
parent 7533 7ee319134713 (current diff)
parent 7627 e1e112687fd6 (diff)
child 7629 d624030abf24
Checking merge against latest trunk
QTfrontend/CMakeLists.txt
QTfrontend/game.cpp
QTfrontend/game.h
QTfrontend/hwform.cpp
QTfrontend/hwform.h
hedgewars/uCommandHandlers.pas
hedgewars/uGears.pas
hedgewars/uIO.pas
hedgewars/uScript.pas
hedgewars/uVariables.pas
--- a/INSTALL	Thu Aug 30 12:47:41 2012 -0400
+++ b/INSTALL	Thu Aug 30 13:02:19 2012 -0400
@@ -18,9 +18,9 @@
 $ cmake .
 or
 $ cmake -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="install_prefix" \
--DDATA_INSTALL_DIR="data_dir" .
+-DDATA_INSTALL_DIR="data_dir" -DNOSERVER=1 .
 
-add -DWITH_SERVER=1 to compile net server; if you have Qt installed but it is
+add -DNOSERVER=0 to compile net server; if you have Qt installed but it is
 not found you can set it up with -DQT_QMAKE_EXECUTABLE="path_to_qmake"
 
 2. Compile:
--- a/QTfrontend/CMakeLists.txt	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/CMakeLists.txt	Thu Aug 30 13:02:19 2012 -0400
@@ -28,6 +28,7 @@
 # Configure for SDL
 find_package(SDL REQUIRED)
 find_package(SDL_mixer REQUIRED)
+find_package(FFMPEG)
 
 include_directories(.)
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/model)
@@ -39,6 +40,7 @@
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/util)
 include_directories(${SDL_INCLUDE_DIR})
 include_directories(${SDLMIXER_INCLUDE_DIR})
+include_directories(${FFMPEG_INCLUDE_DIR})
 include_directories(${CMAKE_SOURCE_DIR}/misc/quazip)
 if(UNIX)
     # HACK: in freebsd cannot find iconv.h included via SDL.h
@@ -71,6 +73,10 @@
 file(GLOB_RECURSE UIcpp ui/*.cpp)
 file(GLOB UtilCpp util/*.cpp)
 
+if((NOT NO_VIDEOREC) AND "${FFMPEG_FOUND}")
+    add_definitions(-DVIDEOREC)
+endif()
+
 set(hwfr_src
     ${ModelCpp}
     ${NetCpp}
@@ -172,6 +178,7 @@
     ${QT_LIBRARIES}
     ${SDL_LIBRARY}
     ${SDLMIXER_LIBRARY}
+    ${FFMPEG_LIBRARIES}
     ${HW_LINK_LIBS}
     )
 
--- a/QTfrontend/binds.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/binds.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -65,5 +65,6 @@
     {"mute",  "8",    QT_TRANSLATE_NOOP("binds", "mute audio"),    NULL, NULL},
     {"fullscr", "f12",  QT_TRANSLATE_NOOP("binds", "change mode"),  NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle fullscreen mode:")},
     {"capture", "c",    QT_TRANSLATE_NOOP("binds", "capture"),  NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Take a screenshot:")},
-    {"rotmask", "delete",   QT_TRANSLATE_NOOP("binds", "hedgehogs\ninfo"),  NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle labels above hedgehogs:")}
+    {"rotmask", "delete",   QT_TRANSLATE_NOOP("binds", "hedgehogs\ninfo"),  NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle labels above hedgehogs:")},
+    {"record",  "r",    QT_TRANSLATE_NOOP("binds", "record"),  NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Record video:")}
 };
--- a/QTfrontend/binds.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/binds.h	Thu Aug 30 13:02:19 2012 -0400
@@ -21,7 +21,7 @@
 
 #include <QString>
 
-#define BINDS_NUMBER 45
+#define BINDS_NUMBER 46
 
 struct BindAction
 {
--- a/QTfrontend/game.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/game.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -53,20 +53,20 @@
 {
     switch (gameType)
     {
-        case gtSave:
-            if (gameState == gsInterrupted || gameState == gsHalted)
-                emit HaveRecord(false, demo);
-            else if (gameState == gsFinished)
-                emit HaveRecord(true, demo);
-            break;
         case gtDemo:
+            // for video recording we need demo anyway 
+            emit HaveRecord(rtNeither, demo);
             break;
         case gtNet:
-            emit HaveRecord(true, demo);
+            emit HaveRecord(rtDemo, demo);
             break;
         default:
-            if (gameState == gsInterrupted || gameState == gsHalted) emit HaveRecord(false, demo);
-            else if (gameState == gsFinished) emit HaveRecord(true, demo);
+            if (gameState == gsInterrupted || gameState == gsHalted)
+                emit HaveRecord(rtSave, demo);
+            else if (gameState == gsFinished)
+                emit HaveRecord(rtDemo, demo);
+            else
+                emit HaveRecord(rtNeither, demo);
     }
     SetGameState(gsStopped);
 }
--- a/QTfrontend/game.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/game.h	Thu Aug 30 13:02:19 2012 -0400
@@ -40,6 +40,13 @@
     gsHalted = 6
 };
 
+enum RecordType
+{
+    rtDemo,
+    rtSave,
+    rtNeither,
+};
+
 bool checkForDir(const QString & dir);
 
 class HWGame : public TCPBase
@@ -70,7 +77,7 @@
         void SendTeamMessage(const QString & msg);
         void GameStateChanged(GameState gameState);
         void GameStats(char type, const QString & info);
-        void HaveRecord(bool isDemo, const QByteArray & record);
+        void HaveRecord(RecordType type, const QByteArray & record);
         void ErrorMessage(const QString &);
         void CampStateChanged(int);
 
--- a/QTfrontend/gameuiconfig.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/gameuiconfig.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -22,14 +22,17 @@
 #include <QDesktopWidget>
 #include <QInputDialog>
 #include <QCryptographicHash>
+#include <QStandardItemModel>
 
 #include "gameuiconfig.h"
 #include "hwform.h"
 #include "pageoptions.h"
+#include "pagevideos.h"
 #include "pagenetserver.h"
 #include "hwconsts.h"
 #include "fpsedit.h"
 #include "HWApplication.h"
+#include "DataManager.h"
 
 GameUIConfig::GameUIConfig(HWForm * FormWidgets, const QString & fileName)
     : QSettings(fileName, QSettings::IniFormat)
@@ -42,6 +45,7 @@
     resizeToConfigValues();
 
     reloadValues();
+    reloadVideosValues();
 }
 
 void GameUIConfig::reloadValues(void)
@@ -108,6 +112,35 @@
     depth = HWApplication::desktop()->depth();
     if (depth < 16) depth = 16;
     else if (depth > 16) depth = 32;
+
+    { // load colors
+        QStandardItemModel * model = DataManager::instance().colorsModel();
+        for(int i = model->rowCount() - 1; i >= 0; --i)
+            model->item(i)->setData(QColor(value(QString("colors/color%1").arg(i), model->item(i)->data().value<QColor>()).value<QColor>()));
+    }
+}
+
+void GameUIConfig::reloadVideosValues(void)
+{
+    Form->ui.pageVideos->framerateBox->setValue(value("videorec/fps",25).toUInt());
+    bool useGameRes = value("videorec/usegameres",true).toBool();
+    if (useGameRes)
+    {
+        QRect res = vid_Resolution();
+        Form->ui.pageVideos->widthEdit->setText(QString::number(res.width()));
+        Form->ui.pageVideos->heightEdit->setText(QString::number(res.height()));
+    }
+    else
+    {
+        Form->ui.pageVideos->widthEdit->setText(value("videorec/width","800").toString());
+        Form->ui.pageVideos->heightEdit->setText(value("videorec/height","600").toString());
+    }
+    Form->ui.pageVideos->checkUseGameRes->setChecked(useGameRes);
+    Form->ui.pageVideos->checkRecordAudio->setChecked(value("videorec/audio",true).toBool());
+    if (!Form->ui.pageVideos->tryCodecs(value("videorec/format","no").toString(),
+                                        value("videorec/videocodec","no").toString(),
+                                        value("videorec/audiocodec","no").toString()))
+        Form->ui.pageVideos->setDefaultCodecs();
 }
 
 QStringList GameUIConfig::GetTeamsList()
@@ -182,6 +215,28 @@
 #ifdef SPARKLE_ENABLED
     setValue("misc/autoUpdate", isAutoUpdateEnabled());
 #endif
+
+    { // save colors
+        QStandardItemModel * model = DataManager::instance().colorsModel();
+        for(int i = model->rowCount() - 1; i >= 0; --i)
+            setValue(QString("colors/color%1").arg(i), model->item(i)->data());
+    }
+
+    Form->gameSettings->sync();
+}
+
+void GameUIConfig::SaveVideosOptions()
+{
+    QRect res = rec_Resolution();
+    setValue("videorec/format", AVFormat());
+    setValue("videorec/videocodec", videoCodec());
+    setValue("videorec/audiocodec", audioCodec());
+    setValue("videorec/fps", rec_Framerate());
+    setValue("videorec/width", res.width());
+    setValue("videorec/height", res.height());
+    setValue("videorec/usegameres", Form->ui.pageVideos->checkUseGameRes->isChecked());
+    setValue("videorec/audio", recordAudio());
+
     Form->gameSettings->sync();
 }
 
@@ -380,3 +435,38 @@
 {
     return Form->ui.pageOptions->volumeBox->value() * 128 / 100;
 }
+
+QString GameUIConfig::AVFormat()
+{
+    return Form->ui.pageVideos->format();
+}
+
+QString GameUIConfig::videoCodec()
+{
+    return Form->ui.pageVideos->videoCodec();
+}
+
+QString GameUIConfig::audioCodec()
+{
+    return Form->ui.pageVideos->audioCodec();
+}
+
+QRect GameUIConfig::rec_Resolution()
+{
+    if (Form->ui.pageVideos->checkUseGameRes->isChecked())
+        return vid_Resolution();
+    QRect res(0,0,0,0);
+    res.setWidth(Form->ui.pageVideos->widthEdit->text().toUInt());
+    res.setHeight(Form->ui.pageVideos->heightEdit->text().toUInt());
+    return res;
+}
+
+int GameUIConfig::rec_Framerate()
+{
+    return Form->ui.pageVideos->framerateBox->value();
+}
+
+bool GameUIConfig::recordAudio()
+{
+    return Form->ui.pageVideos->checkRecordAudio->isChecked();
+}
--- a/QTfrontend/gameuiconfig.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/gameuiconfig.h	Thu Aug 30 13:02:19 2012 -0400
@@ -59,18 +59,27 @@
         void resizeToConfigValues();
         quint32 stereoMode() const;
 
+        QString AVFormat();
+        QString videoCodec();
+        QString audioCodec();
+        QRect rec_Resolution();
+        int rec_Framerate();
+        bool recordAudio();
+
 #ifdef __APPLE__
 #ifdef SPARKLE_ENABLED
         bool isAutoUpdateEnabled();
 #endif
 #endif
-        void reloadValues(void);
+        void reloadValues();
+        void reloadVideosValues();
 
     signals:
         void frontendFullscreen(bool value);
 
     public slots:
         void SaveOptions();
+        void SaveVideosOptions();
         void updNetNick();
     private:
         bool netPasswordIsValid();
--- a/QTfrontend/hwform.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/hwform.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -77,6 +77,7 @@
 #include "pagegamestats.h"
 #include "pageplayrecord.h"
 #include "pagedata.h"
+#include "pagevideos.h"
 #include "hwconsts.h"
 #include "newnetclient.h"
 #include "gamecfgwidget.h"
@@ -91,6 +92,7 @@
 #include "drawmapwidget.h"
 #include "mouseoverfilter.h"
 #include "roomslistmodel.h"
+#include "recorder.h"
 
 #include "DataManager.h"
 
@@ -141,6 +143,8 @@
 
     config = new GameUIConfig(this, cfgdir->absolutePath() + "/hedgewars.ini");
 
+    ui.pageVideos->init(config);
+
 #ifdef __APPLE__
     panel = new M3Panel;
 
@@ -200,12 +204,17 @@
     connect(ui.pageNetGame, SIGNAL(DLCClicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageNetGame, ID_PAGE_DATADOWNLOAD);
 
+#ifdef VIDEOREC
+    connect(ui.pageMain->BtnVideos, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
+    pageSwitchMapper->setMapping(ui.pageMain->BtnVideos, ID_PAGE_VIDEOS);
+#endif
+
     //connect(ui.pageMain->BtnExit, SIGNAL(pressed()), this, SLOT(btnExitPressed()));
     //connect(ui.pageMain->BtnExit, SIGNAL(clicked()), this, SLOT(btnExitClicked()));
 
     connect(ui.pageFeedback->BtnSend, SIGNAL(clicked()), this, SLOT(SendFeedback()));
 
-    connect(ui.pageEditTeam, SIGNAL(teamEdited()), this, SLOT(AfterTeamEdit()));
+    connect(ui.pageEditTeam, SIGNAL(goBack()), this, SLOT(AfterTeamEdit()));
 
     connect(ui.pageMultiplayer->BtnStartMPGame, SIGNAL(clicked()), this, SLOT(StartMPGame()));
     connect(ui.pageMultiplayer->teamsSelect, SIGNAL(setEnabledGameStart(bool)),
@@ -291,6 +300,7 @@
 
     connect(ui.pageConnecting, SIGNAL(cancelConnection()), this, SLOT(GoBack()));
 
+    connect(ui.pageVideos, SIGNAL(goBack()), config, SLOT(SaveVideosOptions()));
 
     ammoSchemeModel = new AmmoSchemeModel(this, cfgdir->absolutePath() + "/schemes.ini");
     ui.pageScheme->setModel(ammoSchemeModel);
@@ -515,6 +525,11 @@
     GoToPage(ID_PAGE_SCHEME);
 }
 
+void HWForm::GoToVideos()
+{
+    GoToPage(ID_PAGE_VIDEOS);
+}
+
 void HWForm::OnPageShown(quint8 id, quint8 lastid)
 {
 #ifdef USE_XFIRE
@@ -606,6 +621,11 @@
         config->reloadValues();
     }
 
+    if (id == ID_PAGE_VIDEOS )
+    {
+        config->reloadVideosValues();
+    }
+
     // load and save ignore/friends lists
     if (lastid == ID_PAGE_NETGAME) // leaving a room
         ui.pageNetGame->pChatWidget->saveLists(ui.pageOptions->editNetNick->text());
@@ -707,6 +727,8 @@
     int curid = ui.Pages->currentIndex();
     if (curid == ID_PAGE_MAIN)
     {
+        if (!ui.pageVideos->tryQuit(this))
+            return;
         stopAnim = true;
         exit();
     }
@@ -881,7 +903,7 @@
 void HWForm::AfterTeamEdit()
 {
     UpdateTeamsLists();
-    GoBack();
+    //GoBack();
 }
 
 
@@ -1361,7 +1383,7 @@
     connect(game, SIGNAL(GameStateChanged(GameState)), this, SLOT(GameStateChanged(GameState)));
     connect(game, SIGNAL(GameStats(char, const QString &)), ui.pageGameStats, SLOT(GameStats(char, const QString &)));
     connect(game, SIGNAL(ErrorMessage(const QString &)), this, SLOT(ShowErrorMessage(const QString &)), Qt::QueuedConnection);
-    connect(game, SIGNAL(HaveRecord(bool, const QByteArray &)), this, SLOT(GetRecord(bool, const QByteArray &)));
+    connect(game, SIGNAL(HaveRecord(RecordType, const QByteArray &)), this, SLOT(GetRecord(RecordType, const QByteArray &)));
     m_lastDemo = QByteArray();
 }
 
@@ -1372,43 +1394,47 @@
                          msg);
 }
 
-void HWForm::GetRecord(bool isDemo, const QByteArray & record)
+void HWForm::GetRecord(RecordType type, const QByteArray & record)
 {
-    QString filename;
-    QByteArray demo = record;
-    QString recordFileName =
-        config->appendDateTimeToRecordName() ?
-        QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm") :
-        "LastRound";
+    if (type != rtNeither)
+    {
+        QString filename;
+        QByteArray demo = record;
+        QString recordFileName =
+            config->appendDateTimeToRecordName() ?
+            QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm") :
+            "LastRound";
 
-    QStringList versionParts = cVersionString->split('-');
-    if ( (versionParts.size() == 2) && (!versionParts[1].isEmpty()) && (versionParts[1].contains(':')) )
-        recordFileName = recordFileName + "_" + versionParts[1].replace(':','-');
+        QStringList versionParts = cVersionString->split('-');
+        if ( (versionParts.size() == 2) && (!versionParts[1].isEmpty()) && (versionParts[1].contains(':')) )
+            recordFileName = recordFileName + "_" + versionParts[1].replace(':','-');
 
-    if (isDemo)
-    {
-        demo.replace(QByteArray("\x02TL"), QByteArray("\x02TD"));
-        demo.replace(QByteArray("\x02TN"), QByteArray("\x02TD"));
-        demo.replace(QByteArray("\x02TS"), QByteArray("\x02TD"));
-        filename = cfgdir->absolutePath() + "/Demos/" + recordFileName + "." + *cProtoVer + ".hwd";
-        m_lastDemo = demo;
-    }
-    else
-    {
-        demo.replace(QByteArray("\x02TL"), QByteArray("\x02TS"));
-        demo.replace(QByteArray("\x02TN"), QByteArray("\x02TS"));
-        filename = cfgdir->absolutePath() + "/Saves/" + recordFileName + "." + *cProtoVer + ".hws";
+        if (type == rtDemo)
+        {
+            demo.replace(QByteArray("\x02TL"), QByteArray("\x02TD"));
+            demo.replace(QByteArray("\x02TN"), QByteArray("\x02TD"));
+            demo.replace(QByteArray("\x02TS"), QByteArray("\x02TD"));
+            filename = cfgdir->absolutePath() + "/Demos/" + recordFileName + "." + *cProtoVer + ".hwd";
+            m_lastDemo = demo;
+        }
+        else
+        {
+            demo.replace(QByteArray("\x02TL"), QByteArray("\x02TS"));
+            demo.replace(QByteArray("\x02TN"), QByteArray("\x02TS"));
+            filename = cfgdir->absolutePath() + "/Saves/" + recordFileName + "." + *cProtoVer + ".hws";
+        }
+
+        QFile demofile(filename);
+        if (!demofile.open(QIODevice::WriteOnly))
+            ShowErrorMessage(tr("Cannot save record to file %1").arg(filename));
+        else
+        {
+            demofile.write(demo);
+            demofile.close();
+        }
     }
 
-
-    QFile demofile(filename);
-    if (!demofile.open(QIODevice::WriteOnly))
-    {
-        ShowErrorMessage(tr("Cannot save record to file %1").arg(filename));
-        return ;
-    }
-    demofile.write(demo);
-    demofile.close();
+    ui.pageVideos->startEncoding(record);
 }
 
 void HWForm::startTraining(const QString & scriptName)
@@ -1455,6 +1481,7 @@
     xfire_free();
 #endif
     config->SaveOptions();
+    config->SaveVideosOptions();
     event->accept();
 }
 
--- a/QTfrontend/hwform.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/hwform.h	Thu Aug 30 13:02:19 2012 -0400
@@ -67,6 +67,7 @@
         void exit();
         void setButtonDescription(QString desc);
         void backDescription();
+        void GoToVideos();
 
     private slots:
         void GoToSaves();
@@ -114,7 +115,7 @@
         void GameStateChanged(GameState gameState);
         void ForcedDisconnect(const QString & reason);
         void ShowErrorMessage(const QString &);
-        void GetRecord(bool isDemo, const QByteArray & record);
+        void GetRecord(RecordType type, const QByteArray & record);
         void CreateNetGame();
         void UpdateWeapons();
         void onFrontendFullscreen(bool value);
@@ -177,6 +178,7 @@
             ID_PAGE_DRAWMAP        ,
             ID_PAGE_DATADOWNLOAD   ,
             ID_PAGE_FEEDBACK	   ,
+            ID_PAGE_VIDEOS,
 	    MAX_PAGE
         };
         QPointer<HWGame> game;
--- a/QTfrontend/main.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/main.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -197,6 +197,8 @@
         checkForDir(cfgdir->absolutePath() + "/Screenshots");
         checkForDir(cfgdir->absolutePath() + "/Teams");
         checkForDir(cfgdir->absolutePath() + "/Logs");
+        checkForDir(cfgdir->absolutePath() + "/Videos");
+        checkForDir(cfgdir->absolutePath() + "/VideoTemp");
     }
 
     datadir->cd(bindir->absolutePath());
--- a/QTfrontend/model/ammoSchemeModel.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/model/ammoSchemeModel.h	Thu Aug 30 13:02:19 2012 -0400
@@ -47,8 +47,8 @@
     public slots:
         void Save();
 
-    signals:
-        void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
+//    signals:
+//        void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
 
     protected:
         QList< QList<QVariant> > schemes;
--- a/QTfrontend/net/newnetclient.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/net/newnetclient.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -384,38 +384,6 @@
         return;
     }
 
-    if (lst[0] == "ADD_TEAM")
-    {
-        if(lst.size() != 24)
-        {
-            qWarning("Net: Bad ADDTEAM message");
-            return;
-        }
-        QStringList tmp = lst;
-        tmp.removeFirst();
-        emit AddNetTeam(tmp);
-        return;
-    }
-
-    if (lst[0] == "REMOVE_TEAM")
-    {
-        if(lst.size() != 2)
-        {
-            qWarning("Net: Bad REMOVETEAM message");
-            return;
-        }
-        emit RemoveNetTeam(HWTeam(lst[1]));
-        return;
-    }
-
-    if(lst[0] == "ROOMABANDONED")
-    {
-        netClientState = InLobby;
-        askRoomsList();
-        emit LeftRoom(tr("Room destroyed"));
-        return;
-    }
-
     if(lst[0] == "KICKED")
     {
         netClientState = InLobby;
@@ -424,31 +392,6 @@
         return;
     }
 
-    if(lst[0] == "JOINED")
-    {
-        if(lst.size() < 2)
-        {
-            qWarning("Net: Bad JOINED message");
-            return;
-        }
-
-        for(int i = 1; i < lst.size(); ++i)
-        {
-            if (lst[i] == mynick)
-            {
-                netClientState = InRoom;
-                emit EnteredGame();
-                emit roomMaster(isChief);
-                if (isChief)
-                    emit configAsked();
-            }
-
-            emit nickAdded(lst[i], isChief && (lst[i] != mynick));
-            emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
-        }
-        return;
-    }
-
     if(lst[0] == "LOBBY:JOINED")
     {
         if(lst.size() < 2)
@@ -472,21 +415,6 @@
         return;
     }
 
-    if(lst[0] == "LEFT")
-    {
-        if(lst.size() < 2)
-        {
-            qWarning("Net: Bad LEFT message");
-            return;
-        }
-        emit nickRemoved(lst[1]);
-        if (lst.size() < 3)
-            emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
-        else
-            emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
-        return;
-    }
-
     if(lst[0] == "ROOM" && lst.size() == 10 && lst[1] == "ADD")
     {
         QStringList tmp = lst;
@@ -529,13 +457,6 @@
         return;
     }
 
-    if (lst[0] == "RUN_GAME")
-    {
-        netClientState = InGame;
-        emit AskForRunGame();
-        return;
-    }
-
     if (lst[0] == "ASKPASSWORD")
     {
         emit AskForPassword(mynick);
@@ -563,76 +484,6 @@
         return;
     }
 
-    if (lst[0] == "TEAM_ACCEPTED")
-    {
-        if (lst.size() != 2)
-        {
-            qWarning("Net: Bad TEAM_ACCEPTED message");
-            return;
-        }
-        emit TeamAccepted(lst[1]);
-        return;
-    }
-
-
-    if (lst[0] == "CFG")
-    {
-        if(lst.size() < 3)
-        {
-            qWarning("Net: Bad CFG message");
-            return;
-        }
-        QStringList tmp = lst;
-        tmp.removeFirst();
-        tmp.removeFirst();
-        if (lst[1] == "SCHEME")
-            emit netSchemeConfig(tmp);
-        else
-            emit paramChanged(lst[1], tmp);
-        return;
-    }
-
-    if (lst[0] == "HH_NUM")
-    {
-        if (lst.size() != 3)
-        {
-            qWarning("Net: Bad TEAM_ACCEPTED message");
-            return;
-        }
-        HWTeam tmptm(lst[1]);
-        tmptm.setNumHedgehogs(lst[2].toUInt());
-        emit hhnumChanged(tmptm);
-        return;
-    }
-
-    if (lst[0] == "TEAM_COLOR")
-    {
-        if (lst.size() != 3)
-        {
-            qWarning("Net: Bad TEAM_COLOR message");
-            return;
-        }
-        HWTeam tmptm(lst[1]);
-        tmptm.setColor(lst[2].toInt());
-        emit teamColorChanged(tmptm);
-        return;
-    }
-
-    if (lst[0] == "EM")
-    {
-        if(lst.size() < 2)
-        {
-            qWarning("Net: Bad EM message");
-            return;
-        }
-        for(int i = 1; i < lst.size(); ++i)
-        {
-            QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
-            emit FromNet(em);
-        }
-        return;
-    }
-
     if (lst[0] == "BYE")
     {
         if (lst.size() < 2)
@@ -650,26 +501,192 @@
         return;
     }
 
-
     if (lst[0] == "ADMIN_ACCESS")
     {
         emit adminAccess(true);
         return;
     }
 
-    if (lst[0] == "ROOM_CONTROL_ACCESS")
+    if(netClientState == InLobby && lst[0] == "JOINED")
     {
-        if (lst.size() < 2)
+        if(lst.size() < 2 || lst[1] != mynick)
         {
-            qWarning("Net: Bad ROOM_CONTROL_ACCESS message");
+            qWarning("Net: Bad JOINED message");
             return;
         }
-        isChief = (lst[1] != "0");
-        emit roomMaster(isChief);
+
+        for(int i = 1; i < lst.size(); ++i)
+        {
+            if (lst[i] == mynick)
+            {
+                netClientState = InRoom;
+                emit EnteredGame();
+                emit roomMaster(isChief);
+                if (isChief)
+                    emit configAsked();
+            }
+
+            emit nickAdded(lst[i], isChief && (lst[i] != mynick));
+            emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
+        }
         return;
     }
 
-    qWarning() << "Net: Unknown message:" << lst;
+    if(netClientState == InRoom || netClientState == InGame)
+    {
+        if (lst[0] == "EM")
+        {
+            if(lst.size() < 2)
+            {
+                qWarning("Net: Bad EM message");
+                return;
+            }
+            for(int i = 1; i < lst.size(); ++i)
+            {
+                QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
+                emit FromNet(em);
+            }
+            return;
+        }
+
+        if (lst[0] == "ADD_TEAM")
+        {
+            if(lst.size() != 24)
+            {
+                qWarning("Net: Bad ADDTEAM message");
+                return;
+            }
+            QStringList tmp = lst;
+            tmp.removeFirst();
+            emit AddNetTeam(tmp);
+            return;
+        }
+
+        if (lst[0] == "REMOVE_TEAM")
+        {
+            if(lst.size() != 2)
+            {
+                qWarning("Net: Bad REMOVETEAM message");
+                return;
+            }
+            emit RemoveNetTeam(HWTeam(lst[1]));
+            return;
+        }
+
+        if(lst[0] == "ROOMABANDONED")
+        {
+            netClientState = InLobby;
+            askRoomsList();
+            emit LeftRoom(tr("Room destroyed"));
+            return;
+        }
+
+        if (lst[0] == "RUN_GAME")
+        {
+            netClientState = InGame;
+            emit AskForRunGame();
+            return;
+        }
+
+        if (lst[0] == "TEAM_ACCEPTED")
+        {
+            if (lst.size() != 2)
+            {
+                qWarning("Net: Bad TEAM_ACCEPTED message");
+                return;
+            }
+            emit TeamAccepted(lst[1]);
+            return;
+        }
+
+        if (lst[0] == "CFG")
+        {
+            if(lst.size() < 3)
+            {
+                qWarning("Net: Bad CFG message");
+                return;
+            }
+            QStringList tmp = lst;
+            tmp.removeFirst();
+            tmp.removeFirst();
+            if (lst[1] == "SCHEME")
+                emit netSchemeConfig(tmp);
+            else
+                emit paramChanged(lst[1], tmp);
+            return;
+        }
+
+        if (lst[0] == "HH_NUM")
+        {
+            if (lst.size() != 3)
+            {
+                qWarning("Net: Bad TEAM_ACCEPTED message");
+                return;
+            }
+            HWTeam tmptm(lst[1]);
+            tmptm.setNumHedgehogs(lst[2].toUInt());
+            emit hhnumChanged(tmptm);
+            return;
+        }
+
+        if (lst[0] == "TEAM_COLOR")
+        {
+            if (lst.size() != 3)
+            {
+                qWarning("Net: Bad TEAM_COLOR message");
+                return;
+            }
+            HWTeam tmptm(lst[1]);
+            tmptm.setColor(lst[2].toInt());
+            emit teamColorChanged(tmptm);
+            return;
+        }
+
+        if(lst[0] == "JOINED")
+        {
+            if(lst.size() < 2)
+            {
+                qWarning("Net: Bad JOINED message");
+                return;
+            }
+
+            for(int i = 1; i < lst.size(); ++i)
+            {
+                emit nickAdded(lst[i], isChief && (lst[i] != mynick));
+                emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
+            }
+            return;
+        }
+
+        if(lst[0] == "LEFT")
+        {
+            if(lst.size() < 2)
+            {
+                qWarning("Net: Bad LEFT message");
+                return;
+            }
+            emit nickRemoved(lst[1]);
+            if (lst.size() < 3)
+                emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
+            else
+                emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
+            return;
+        }
+
+        if (lst[0] == "ROOM_CONTROL_ACCESS")
+        {
+            if (lst.size() < 2)
+            {
+                qWarning("Net: Bad ROOM_CONTROL_ACCESS message");
+                return;
+            }
+            isChief = (lst[1] != "0");
+            emit roomMaster(isChief);
+            return;
+        }
+    }
+
+    qWarning() << "Net: Unknown message or wrong state:" << lst;
 }
 
 void HWNewNet::onHedgehogsNumChanged(const HWTeam& team)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/net/recorder.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,129 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QString>
+#include <QByteArray>
+//#include <QMessageBox>
+
+#include "recorder.h"
+#include "gameuiconfig.h"
+#include "hwconsts.h"
+#include "game.h"
+#include "libav_iteraction.h"
+
+// Encoding is memory expensive process, so we need to limit maximum number
+// of simultaneous encoders.
+static const int maxRecorders = 3;
+static int numRecorders = 0;
+
+static QList<HWRecorder*> queue;
+
+HWRecorder::HWRecorder(GameUIConfig * config, const QString &prefix) :
+    TCPBase(false)
+{
+    this->config = config;
+    this->prefix = prefix;
+    finished = false;
+    name = prefix + "." + LibavIteraction::instance().getExtension(config->AVFormat());
+}
+
+HWRecorder::~HWRecorder()
+{
+    emit encodingFinished(finished);
+    if (queue.empty())
+        numRecorders--;
+    else
+        queue.takeFirst()->Start();
+}
+
+void HWRecorder::onClientDisconnect()
+{
+}
+
+void HWRecorder::onClientRead()
+{
+    quint8 msglen;
+    quint32 bufsize;
+    while (!readbuffer.isEmpty() && ((bufsize = readbuffer.size()) > 0) &&
+            ((msglen = readbuffer.data()[0]) < bufsize))
+    {
+        QByteArray msg = readbuffer.left(msglen + 1);
+        readbuffer.remove(0, msglen + 1);
+        switch (msg.at(1))
+        {
+        case '?':
+            SendIPC("!");
+            break;
+        case 'p':
+            emit onProgress((quint8(msg.at(2))*256.0 + quint8(msg.at(3)))*0.0001);
+            break;
+        case 'v':
+            finished = true;
+            break;
+        }
+    }
+}
+
+void HWRecorder::EncodeVideo(const QByteArray & record)
+{
+    toSendBuf = record;
+    toSendBuf.replace(QByteArray("\x02TD"), QByteArray("\x02TV"));
+    toSendBuf.replace(QByteArray("\x02TL"), QByteArray("\x02TV"));
+    toSendBuf.replace(QByteArray("\x02TN"), QByteArray("\x02TV"));
+    toSendBuf.replace(QByteArray("\x02TS"), QByteArray("\x02TV"));
+
+    if (numRecorders < maxRecorders)
+    {
+        numRecorders++;
+        Start(); // run engine
+    }
+    else
+        queue.push_back(this);
+}
+
+QStringList HWRecorder::getArguments()
+{
+    QStringList arguments;
+    QRect resolution = config->rec_Resolution();
+    arguments << cfgdir->absolutePath();
+    arguments << QString::number(resolution.width());
+    arguments << QString::number(resolution.height());
+    arguments << "32"; // bpp
+    arguments << QString("%1").arg(ipc_port);
+    arguments << "0"; // fullscreen
+    arguments << "0"; // sound
+    arguments << "0"; // music
+    arguments << "0"; // sound volume
+    arguments << QString::number(config->timerInterval());
+    arguments << datadir->absolutePath();
+    arguments << (config->isShowFPSEnabled() ? "1" : "0");
+    arguments << (config->isAltDamageEnabled() ? "1" : "0");
+    arguments << config->netNick().toUtf8().toBase64();
+    arguments << QString::number(config->translateQuality());
+    arguments << QString::number(config->stereoMode());
+    arguments << HWGame::tr("en.txt");
+    arguments << QString::number(config->rec_Framerate()); // framerate numerator
+    arguments << "1";  // framerate denominator
+    arguments << prefix;
+    arguments << config->AVFormat();
+    arguments << config->videoCodec();
+    arguments << "5"; // video quality
+    arguments << (config->recordAudio()? config->audioCodec() : "no");
+
+    return arguments;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/net/recorder.h	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,58 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef RECORDER_H
+#define RECORDER_H
+
+#include <QString>
+#include <QByteArray>
+
+#include "tcpBase.h"
+
+class GameUIConfig;
+class VideoItem;
+
+class HWRecorder : public TCPBase
+{
+        Q_OBJECT
+    public:
+        HWRecorder(GameUIConfig * config, const QString & prefix);
+        virtual ~HWRecorder();
+
+        void EncodeVideo(const QByteArray & record);
+
+        VideoItem * item; // used by pagevideos
+        QString name;
+        QString prefix;
+
+    protected:
+        // virtuals from TCPBase
+        virtual QStringList getArguments();
+        virtual void onClientRead();
+        virtual void onClientDisconnect();
+
+    signals:
+        void onProgress(float progress); // 0 < progress < 1
+        void encodingFinished(bool success);
+
+    private:
+        bool finished;
+        GameUIConfig * config;
+};
+
+#endif // RECORDER_H
--- a/QTfrontend/net/tcpBase.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/net/tcpBase.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -31,6 +31,8 @@
 
 TCPBase::~TCPBase()
 {
+    if (IPCSocket)
+        IPCSocket->deleteLater();
 }
 
 TCPBase::TCPBase(bool demoMode) :
@@ -65,6 +67,9 @@
     connect(IPCSocket, SIGNAL(disconnected()), this, SLOT(ClientDisconnect()));
     connect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
     SendToClientFirst();
+
+    if(srvsList.size()==1) srvsList.pop_front();
+    emit isReadyNow();
 }
 
 void TCPBase::RealStart()
@@ -88,8 +93,8 @@
     disconnect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
     onClientDisconnect();
 
-    if(srvsList.size()==1) srvsList.pop_front();
-    emit isReadyNow();
+ /*   if(srvsList.size()==1) srvsList.pop_front();
+    emit isReadyNow();*/
     IPCSocket->deleteLater();
     deleteLater();
 }
@@ -107,6 +112,8 @@
     QMessageBox::critical(0, tr("Error"),
                           tr("Unable to run engine: %1 (")
                           .arg(error) + bindir->absolutePath() + "/hwengine)");
+
+    ClientDisconnect();
 }
 
 void TCPBase::tcpServerReady()
--- a/QTfrontend/res/css/birthday.css	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/birthday.css	Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/christmas.css	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/christmas.css	Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/easter.css	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/easter.css	Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/qt.css	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/qt.css	Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/ask_quit.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,79 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QTimer>
+
+#include "hwform.h"
+#include "ask_quit.h"
+#include "pagevideos.h"
+
+HWAskQuitDialog::HWAskQuitDialog(QWidget* parent, HWForm * form) : QDialog(parent)
+{
+    this->form = form;
+
+    setWindowTitle(tr("Do yot really want to quit?"));
+
+    QVBoxLayout * layout = new QVBoxLayout(this);
+
+    QLabel * lbLabel = new QLabel(this);
+    lbLabel->setText(QLabel::tr("There are videos that are currently being processed.\n"
+                                "Exiting now will abort them.\n"
+                                "Do yot really want to quit?"));
+    layout->addWidget(lbLabel);
+
+    lbList = new QLabel(this);
+    layout->addWidget(lbList);
+    updateList();
+
+    QDialogButtonBox* dbbButtons = new QDialogButtonBox(this);
+    QPushButton * pbYes = dbbButtons->addButton(QDialogButtonBox::Yes);
+    QPushButton * pbNo  = dbbButtons->addButton(QDialogButtonBox::No);
+    QPushButton * pbMore = dbbButtons->addButton(QPushButton::tr("More info"), QDialogButtonBox::HelpRole);
+    layout->addWidget(dbbButtons);
+
+    connect(pbYes,  SIGNAL(clicked()), this, SLOT(accept()));
+    connect(pbNo,   SIGNAL(clicked()), this, SLOT(reject()));
+    connect(pbMore, SIGNAL(clicked()), this, SLOT(goToPageVideos()));
+
+    // update list periodically
+    QTimer * timer = new QTimer(this);
+    connect(timer, SIGNAL(timeout()), this, SLOT(updateList()));
+    timer->start(200);
+}
+
+void HWAskQuitDialog::goToPageVideos()
+{
+    reject();
+    form->GoToVideos();
+}
+
+void HWAskQuitDialog::updateList()
+{
+    QString text = form->ui.pageVideos->getVideosInProgress();
+    if (text.isEmpty())
+    {
+        // automatically exit when everything is finished
+        accept();
+        return;
+    }
+    lbList->setText(text);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/ask_quit.h	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,45 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef ASK_QUIT_H
+#define ASK_QUIT_H
+
+#include <QDialog>
+
+class QLabel;
+class HWForm;
+class PageVideos;
+
+class HWAskQuitDialog : public QDialog
+{
+        Q_OBJECT
+
+    public:
+        HWAskQuitDialog(QWidget* parent, HWForm *form);
+
+    private slots:
+        void goToPageVideos();
+        void updateList();
+
+    private:
+        HWForm * form;
+        QLabel * lbList;
+};
+
+
+#endif // INPUT_PASSWORD_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/upload_video.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,297 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <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 <QMessageBox>
+
+#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=\"http://www.youtube.com/t/terms\" style=\"color: white;\">http://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()));
+}
+
+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::warning(this, QMessageBox::tr("Error"), errorStr);
+        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::fromAscii(reply->rawHeader("Location"));
+    if (location.isEmpty())
+    {
+        QString errorStr = QMessageBox::tr("Error while sending metadata to youtube.com:\n");
+        errorStr += reply->errorString();
+        QMessageBox::warning(this, QMessageBox::tr("Error"), errorStr);
+        setEditable(true);
+        return;
+    }
+
+    accept();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/upload_video.h	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,65 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef 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/pageeditteam.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageeditteam.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -289,6 +289,9 @@
     int idx = list.indexOf("cpu.png");
     if (idx >= 0)
         list.removeAt(idx);
+    idx = list.indexOf("cpu_plain.png");
+    if (idx >= 0)
+        list.removeAt(idx);
     idx = list.indexOf("hedgewars.png");
     if (idx >= 0)
         list.removeAt(idx);
@@ -469,5 +472,4 @@
 void PageEditTeam::saveTeam()
 {
     data().saveToFile();
-    emit teamEdited();
 }
--- a/QTfrontend/ui/page/pageeditteam.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageeditteam.h	Thu Aug 30 13:02:19 2012 -0400
@@ -40,9 +40,6 @@
         void editTeam(const QString & name, const QString & playerHash);
         void deleteTeam(const QString & name);
 
-    signals:
-        void teamEdited();
-
     public slots:
         void CBFort_activated(const QString & gravename);
 
--- a/QTfrontend/ui/page/pagemain.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pagemain.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -85,9 +85,15 @@
     bottomLayout->setStretch(0,1);
 
     btnBack->setWhatsThis(tr("Exit game"));
-    
-    BtnSetup = addButton(":/res/Settings.png", bottomLayout, 1, true);
+
+#ifdef VIDEOREC
+    BtnVideos = addButton(":/res/Record.png", bottomLayout, 1, true);
+    BtnVideos->setWhatsThis(tr("Manage videos recorded from game"));
+#endif
+
+    BtnSetup = addButton(":/res/Settings.png", bottomLayout, 2, true);
     BtnSetup->setWhatsThis(tr("Edit game preferences"));
+
     return bottomLayout;
 }
 
--- a/QTfrontend/ui/page/pagemain.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pagemain.h	Thu Aug 30 13:02:19 2012 -0400
@@ -34,6 +34,7 @@
         QPushButton * BtnFeedback;
         QPushButton * BtnInfo;
         QPushButton * BtnDataDownload;
+        QPushButton * BtnVideos;
         QLabel * mainNote;
 
     private:
--- a/QTfrontend/ui/page/pageoptions.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageoptions.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -28,368 +28,397 @@
 #include <QTextBrowser>
 #include <QTableWidget>
 #include <QSlider>
+#include <QSignalMapper>
+#include <QColorDialog>
+#include <QStandardItemModel>
 
 #include "pageoptions.h"
 #include "hwconsts.h"
 #include "fpsedit.h"
 #include "igbox.h"
+#include "DataManager.h"
 
 // TODO cleanup
 QLayout * PageOptions::bodyLayoutDefinition()
 {
-    QGridLayout * pageLayout = new QGridLayout();
-    pageLayout->setColumnStretch(0, 100);
-    pageLayout->setColumnStretch(1, 100);
-    pageLayout->setColumnStretch(2, 100);
-    pageLayout->setRowStretch(0, 0);
-    //pageLayout->setRowStretch(1, 100);
-    pageLayout->setRowStretch(2, 0);
-    pageLayout->setContentsMargins(7, 7, 7, 0);
-    pageLayout->setSpacing(0);
+    QVBoxLayout * pageLayout = new QVBoxLayout();
 
+    QTabWidget * tabs = new QTabWidget(this);
+    pageLayout->addWidget(tabs);
+    QWidget * page1 = new QWidget(this);
+    QWidget * page2 = new QWidget(this);
+    tabs->addTab(page1, tr("General"));
+    tabs->addTab(page2, tr("Advanced"));
+
+    { // page 1
+        QGridLayout * page1Layout = new QGridLayout(page1);
+        //gbTBLayout->setMargin(0);
+        page1Layout->setSpacing(0);
+        page1Layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
 
-    QGroupBox * gbTwoBoxes = new QGroupBox(this);
-    pageLayout->addWidget(gbTwoBoxes, 0, 0, 1, 3);
-    QGridLayout * gbTBLayout = new QGridLayout(gbTwoBoxes);
-    gbTBLayout->setMargin(0);
-    gbTBLayout->setSpacing(0);
-    gbTBLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
-
-    QPixmap pmNew(":/res/new.png");
-    QPixmap pmEdit(":/res/edit.png");
-    QPixmap pmDelete(":/res/delete.png");
+        QPixmap pmNew(":/res/new.png");
+        QPixmap pmEdit(":/res/edit.png");
+        QPixmap pmDelete(":/res/delete.png");
 
-    {
-        teamsBox = new IconedGroupBox(this);
-        //teamsBox->setContentTopPadding(0);
-        //teamsBox->setAttribute(Qt::WA_PaintOnScreen, true);
-        teamsBox->setIcon(QIcon(":/res/teamicon.png"));
-        teamsBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-        teamsBox->setTitle(QGroupBox::tr("Teams"));
+        {
+            teamsBox = new IconedGroupBox(this);
+            //teamsBox->setContentTopPadding(0);
+            //teamsBox->setAttribute(Qt::WA_PaintOnScreen, true);
+            teamsBox->setIcon(QIcon(":/res/teamicon.png"));
+            teamsBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+            teamsBox->setTitle(QGroupBox::tr("Teams"));
 
-        QGridLayout * GBTlayout = new QGridLayout(teamsBox);
+            QGridLayout * GBTlayout = new QGridLayout(teamsBox);
 
-        CBTeamName = new QComboBox(teamsBox);
-        GBTlayout->addWidget(CBTeamName, 0, 0);
+            CBTeamName = new QComboBox(teamsBox);
+            GBTlayout->addWidget(CBTeamName, 0, 0);
 
-        BtnNewTeam = new QPushButton(teamsBox);
-        BtnNewTeam->setToolTip(tr("New team"));
-        BtnNewTeam->setIconSize(pmNew.size());
-        BtnNewTeam->setIcon(pmNew);
-        BtnNewTeam->setMaximumWidth(pmNew.width() + 6);
-        connect(BtnNewTeam, SIGNAL(clicked()), this, SIGNAL(newTeamRequested()));
-        GBTlayout->addWidget(BtnNewTeam, 0, 1);
+            BtnNewTeam = new QPushButton(teamsBox);
+            BtnNewTeam->setToolTip(tr("New team"));
+            BtnNewTeam->setIconSize(pmNew.size());
+            BtnNewTeam->setIcon(pmNew);
+            BtnNewTeam->setMaximumWidth(pmNew.width() + 6);
+            connect(BtnNewTeam, SIGNAL(clicked()), this, SIGNAL(newTeamRequested()));
+            GBTlayout->addWidget(BtnNewTeam, 0, 1);
 
-        BtnEditTeam = new QPushButton(teamsBox);
-        BtnEditTeam->setToolTip(tr("Edit team"));
-        BtnEditTeam->setIconSize(pmEdit.size());
-        BtnEditTeam->setIcon(pmEdit);
-        BtnEditTeam->setMaximumWidth(pmEdit.width() + 6);
-        connect(BtnEditTeam, SIGNAL(clicked()), this, SLOT(requestEditSelectedTeam()));
-        GBTlayout->addWidget(BtnEditTeam, 0, 2);
+            BtnEditTeam = new QPushButton(teamsBox);
+            BtnEditTeam->setToolTip(tr("Edit team"));
+            BtnEditTeam->setIconSize(pmEdit.size());
+            BtnEditTeam->setIcon(pmEdit);
+            BtnEditTeam->setMaximumWidth(pmEdit.width() + 6);
+            connect(BtnEditTeam, SIGNAL(clicked()), this, SLOT(requestEditSelectedTeam()));
+            GBTlayout->addWidget(BtnEditTeam, 0, 2);
 
-        BtnDeleteTeam = new QPushButton(teamsBox);
-        BtnDeleteTeam->setToolTip(tr("Delete team"));
-        BtnDeleteTeam->setIconSize(pmDelete.size());
-        BtnDeleteTeam->setIcon(pmDelete);
-        BtnDeleteTeam->setMaximumWidth(pmDelete.width() + 6);
-        connect(BtnDeleteTeam, SIGNAL(clicked()), this, SLOT(requestDeleteSelectedTeam()));
-        GBTlayout->addWidget(BtnDeleteTeam, 0, 3);
-
-        LblNoEditTeam = new QLabel(teamsBox);
-        LblNoEditTeam->setText(tr("You can't edit teams from team selection. Go back to main menu to add, edit or delete teams."));
-        LblNoEditTeam->setWordWrap(true);
-        LblNoEditTeam->setVisible(false);
-        GBTlayout->addWidget(LblNoEditTeam, 0, 0);
+            BtnDeleteTeam = new QPushButton(teamsBox);
+            BtnDeleteTeam->setToolTip(tr("Delete team"));
+            BtnDeleteTeam->setIconSize(pmDelete.size());
+            BtnDeleteTeam->setIcon(pmDelete);
+            BtnDeleteTeam->setMaximumWidth(pmDelete.width() + 6);
+            connect(BtnDeleteTeam, SIGNAL(clicked()), this, SLOT(requestDeleteSelectedTeam()));
+            GBTlayout->addWidget(BtnDeleteTeam, 0, 3);
 
-        gbTBLayout->addWidget(teamsBox, 0, 0);
-    }
-
-    {
-        IconedGroupBox* groupWeapons = new IconedGroupBox(this);
+            LblNoEditTeam = new QLabel(teamsBox);
+            LblNoEditTeam->setText(tr("You can't edit teams from team selection. Go back to main menu to add, edit or delete teams."));
+            LblNoEditTeam->setWordWrap(true);
+            LblNoEditTeam->setVisible(false);
+            GBTlayout->addWidget(LblNoEditTeam, 0, 0);
 
-        //groupWeapons->setContentTopPadding(0);
-        //groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-        groupWeapons->setIcon(QIcon(":/res/weaponsicon.png"));
-        groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-        groupWeapons->setTitle(QGroupBox::tr("Schemes and Weapons"));
-        QGridLayout * WeaponsLayout = new QGridLayout(groupWeapons);
+            page1Layout->addWidget(teamsBox, 0, 0);
+        }
 
-        QLabel* SchemeLabel = new QLabel(groupWeapons);
-        SchemeLabel->setText(QLabel::tr("Game scheme"));
-        WeaponsLayout->addWidget(SchemeLabel, 1, 0);
+        {
+            IconedGroupBox* groupWeapons = new IconedGroupBox(this);
 
-        SchemesName = new QComboBox(groupWeapons);
-        WeaponsLayout->addWidget(SchemesName, 1, 1);
+            //groupWeapons->setContentTopPadding(0);
+            //groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+            groupWeapons->setIcon(QIcon(":/res/weaponsicon.png"));
+            groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+            groupWeapons->setTitle(QGroupBox::tr("Schemes and Weapons"));
+            QGridLayout * WeaponsLayout = new QGridLayout(groupWeapons);
 
-        SchemeNew = new QPushButton(groupWeapons);
-        SchemeNew->setWhatsThis(tr("New scheme"));
-        SchemeNew->setIconSize(pmNew.size());
-        SchemeNew->setIcon(pmNew);
-        SchemeNew->setMaximumWidth(pmNew.width() + 6);
-        WeaponsLayout->addWidget(SchemeNew, 1, 2);
+            QLabel* SchemeLabel = new QLabel(groupWeapons);
+            SchemeLabel->setText(QLabel::tr("Game scheme"));
+            WeaponsLayout->addWidget(SchemeLabel, 1, 0);
 
-        SchemeEdit = new QPushButton(groupWeapons);
-        SchemeEdit->setWhatsThis(tr("Edit scheme"));
-        SchemeEdit->setIconSize(pmEdit.size());
-        SchemeEdit->setIcon(pmEdit);
-        SchemeEdit->setMaximumWidth(pmEdit.width() + 6);
-        WeaponsLayout->addWidget(SchemeEdit, 1, 3);
+            SchemesName = new QComboBox(groupWeapons);
+            WeaponsLayout->addWidget(SchemesName, 1, 1);
 
-        SchemeDelete = new QPushButton(groupWeapons);
-        SchemeDelete->setWhatsThis(tr("Delete scheme"));
-        SchemeDelete->setIconSize(pmDelete.size());
-        SchemeDelete->setIcon(pmDelete);
-        SchemeDelete->setMaximumWidth(pmDelete.width() + 6);
-        WeaponsLayout->addWidget(SchemeDelete, 1, 4);
+            SchemeNew = new QPushButton(groupWeapons);
+            SchemeNew->setWhatsThis(tr("New scheme"));
+            SchemeNew->setIconSize(pmNew.size());
+            SchemeNew->setIcon(pmNew);
+            SchemeNew->setMaximumWidth(pmNew.width() + 6);
+            WeaponsLayout->addWidget(SchemeNew, 1, 2);
 
-        QLabel* WeaponLabel = new QLabel(groupWeapons);
-        WeaponLabel->setText(QLabel::tr("Weapons"));
-        WeaponsLayout->addWidget(WeaponLabel, 2, 0);
-
-        WeaponsName = new QComboBox(groupWeapons);
-        WeaponsLayout->addWidget(WeaponsName, 2, 1);
-
-        WeaponNew = new QPushButton(groupWeapons);
-        WeaponNew->setWhatsThis(tr("New weapon set"));
-        WeaponNew->setIconSize(pmNew.size());
-        WeaponNew->setIcon(pmNew);
-        WeaponNew->setMaximumWidth(pmNew.width() + 6);
-        WeaponsLayout->addWidget(WeaponNew, 2, 2);
+            SchemeEdit = new QPushButton(groupWeapons);
+            SchemeEdit->setWhatsThis(tr("Edit scheme"));
+            SchemeEdit->setIconSize(pmEdit.size());
+            SchemeEdit->setIcon(pmEdit);
+            SchemeEdit->setMaximumWidth(pmEdit.width() + 6);
+            WeaponsLayout->addWidget(SchemeEdit, 1, 3);
 
-        WeaponEdit = new QPushButton(groupWeapons);
-        WeaponEdit->setWhatsThis(tr("Edit weapon set"));
-        WeaponEdit->setIconSize(pmEdit.size());
-        WeaponEdit->setIcon(pmEdit);
-        WeaponEdit->setMaximumWidth(pmEdit.width() + 6);
-        WeaponsLayout->addWidget(WeaponEdit, 2, 3);
+            SchemeDelete = new QPushButton(groupWeapons);
+            SchemeDelete->setWhatsThis(tr("Delete scheme"));
+            SchemeDelete->setIconSize(pmDelete.size());
+            SchemeDelete->setIcon(pmDelete);
+            SchemeDelete->setMaximumWidth(pmDelete.width() + 6);
+            WeaponsLayout->addWidget(SchemeDelete, 1, 4);
 
-        WeaponDelete = new QPushButton(groupWeapons);
-        WeaponDelete->setWhatsThis(tr("Delete weapon set"));
-        WeaponDelete->setIconSize(pmDelete.size());
-        WeaponDelete->setIcon(pmDelete);
-        WeaponDelete->setMaximumWidth(pmDelete.width() + 6);
-        WeaponsLayout->addWidget(WeaponDelete, 2, 4);
+            QLabel* WeaponLabel = new QLabel(groupWeapons);
+            WeaponLabel->setText(QLabel::tr("Weapons"));
+            WeaponsLayout->addWidget(WeaponLabel, 2, 0);
 
-        WeaponTooltip = new QCheckBox(this);
-        WeaponTooltip->setText(QCheckBox::tr("Show ammo menu tooltips"));
-        WeaponsLayout->addWidget(WeaponTooltip, 3, 0, 1, 4);
-
-        gbTBLayout->addWidget(groupWeapons, 1, 0);
-    }
+            WeaponsName = new QComboBox(groupWeapons);
+            WeaponsLayout->addWidget(WeaponsName, 2, 1);
 
-    {
-        IconedGroupBox* groupMisc = new IconedGroupBox(this);
-        //groupMisc->setContentTopPadding(0);
-        groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
-        groupMisc->setIcon(QIcon(":/res/miscicon.png"));
-        //groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-        groupMisc->setTitle(QGroupBox::tr("Misc"));
-        QGridLayout * MiscLayout = new QGridLayout(groupMisc);
+            WeaponNew = new QPushButton(groupWeapons);
+            WeaponNew->setWhatsThis(tr("New weapon set"));
+            WeaponNew->setIconSize(pmNew.size());
+            WeaponNew->setIcon(pmNew);
+            WeaponNew->setMaximumWidth(pmNew.width() + 6);
+            WeaponsLayout->addWidget(WeaponNew, 2, 2);
 
-        // Label for "Language"
-        QLabel *labelLanguage = new QLabel(groupMisc);
-        labelLanguage->setText(QLabel::tr("Locale") + " *");
-        MiscLayout->addWidget(labelLanguage, 0, 0);
+            WeaponEdit = new QPushButton(groupWeapons);
+            WeaponEdit->setWhatsThis(tr("Edit weapon set"));
+            WeaponEdit->setIconSize(pmEdit.size());
+            WeaponEdit->setIcon(pmEdit);
+            WeaponEdit->setMaximumWidth(pmEdit.width() + 6);
+            WeaponsLayout->addWidget(WeaponEdit, 2, 3);
 
-        // List of installed languages
-        CBLanguage = new QComboBox(groupMisc);
-        QDir tmpdir;
-        tmpdir.cd(cfgdir->absolutePath());
-        tmpdir.cd("Data/Locale");
-        tmpdir.setFilter(QDir::Files);
-        QStringList locs = tmpdir.entryList(QStringList("hedgewars_*.qm"));
-        CBLanguage->addItem(QComboBox::tr("(System default)"), QString(""));
-        for(int i = 0; i < locs.count(); i++)
-        {
-            QLocale loc(locs[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
-            CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+            WeaponDelete = new QPushButton(groupWeapons);
+            WeaponDelete->setWhatsThis(tr("Delete weapon set"));
+            WeaponDelete->setIconSize(pmDelete.size());
+            WeaponDelete->setIcon(pmDelete);
+            WeaponDelete->setMaximumWidth(pmDelete.width() + 6);
+            WeaponsLayout->addWidget(WeaponDelete, 2, 4);
+
+            WeaponTooltip = new QCheckBox(this);
+            WeaponTooltip->setText(QCheckBox::tr("Show ammo menu tooltips"));
+            WeaponsLayout->addWidget(WeaponTooltip, 3, 0, 1, 4);
+
+            page1Layout->addWidget(groupWeapons, 1, 0);
         }
 
-        tmpdir.cd(datadir->absolutePath());
-        tmpdir.cd("Locale");
-        tmpdir.setFilter(QDir::Files);
-        QStringList tmplist = tmpdir.entryList(QStringList("hedgewars_*.qm"));
-        for(int i = 0; i < tmplist.count(); i++)
         {
-            if (locs.contains(tmplist[i])) continue;
-            QLocale loc(tmplist[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
-            CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+            IconedGroupBox* groupMisc = new IconedGroupBox(this);
+            //groupMisc->setContentTopPadding(0);
+            groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
+            groupMisc->setIcon(QIcon(":/res/miscicon.png"));
+            //groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+            groupMisc->setTitle(QGroupBox::tr("Misc"));
+            QGridLayout * MiscLayout = new QGridLayout(groupMisc);
+
+            // Label for "Language"
+            QLabel *labelLanguage = new QLabel(groupMisc);
+            labelLanguage->setText(QLabel::tr("Locale") + " *");
+            MiscLayout->addWidget(labelLanguage, 0, 0);
+
+            // List of installed languages
+            CBLanguage = new QComboBox(groupMisc);
+            QDir tmpdir;
+            tmpdir.cd(cfgdir->absolutePath());
+            tmpdir.cd("Data/Locale");
+            tmpdir.setFilter(QDir::Files);
+            QStringList locs = tmpdir.entryList(QStringList("hedgewars_*.qm"));
+            CBLanguage->addItem(QComboBox::tr("(System default)"), QString(""));
+            for(int i = 0; i < locs.count(); i++)
+            {
+                QLocale loc(locs[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
+                CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+            }
+
+            tmpdir.cd(datadir->absolutePath());
+            tmpdir.cd("Locale");
+            tmpdir.setFilter(QDir::Files);
+            QStringList tmplist = tmpdir.entryList(QStringList("hedgewars_*.qm"));
+            for(int i = 0; i < tmplist.count(); i++)
+            {
+                if (locs.contains(tmplist[i])) continue;
+                QLocale loc(tmplist[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
+                CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+            }
+
+            MiscLayout->addWidget(CBLanguage, 0, 1);
+
+            // Label and field for net nick
+            labelNN = new QLabel(groupMisc);
+            labelNN->setText(QLabel::tr("Nickname"));
+            MiscLayout->addWidget(labelNN, 1, 0);
+
+            editNetNick = new QLineEdit(groupMisc);
+            editNetNick->setMaxLength(20);
+            editNetNick->setText(QLineEdit::tr("anonymous"));
+            MiscLayout->addWidget(editNetNick, 1, 1);
+
+            // checkbox and field for password
+            CBSavePassword = new QCheckBox(groupMisc);
+            CBSavePassword->setText(QCheckBox::tr("Save password"));
+            MiscLayout->addWidget(CBSavePassword, 2, 0);
+
+            editNetPassword = new QLineEdit(groupMisc);
+            editNetPassword->setEchoMode(QLineEdit::Password);
+            MiscLayout->addWidget(editNetPassword, 2, 1);
+
+            CBNameWithDate = new QCheckBox(groupMisc);
+            CBNameWithDate->setText(QCheckBox::tr("Append date and time to record file name"));
+            MiscLayout->addWidget(CBNameWithDate, 5, 0, 1, 2);
+
+            BtnAssociateFiles = new QPushButton(groupMisc);
+            BtnAssociateFiles->setText(QPushButton::tr("Associate file extensions"));
+            BtnAssociateFiles->setVisible(!custom_data && !custom_config);
+            MiscLayout->addWidget(BtnAssociateFiles, 6, 0, 1, 2);
+
+    #ifdef __APPLE__
+    #ifdef SPARKLE_ENABLED
+            CBAutoUpdate = new QCheckBox(groupMisc);
+            CBAutoUpdate->setText(QCheckBox::tr("Check for updates at startup"));
+            MiscLayout->addWidget(CBAutoUpdate, 7, 0, 1, 3);
+    #endif
+    #endif
+            page1Layout->addWidget(groupMisc, 2, 0);
         }
 
-        MiscLayout->addWidget(CBLanguage, 0, 1);
+        {
+            AGGroupBox = new IconedGroupBox(this);
+            //AGGroupBox->setContentTopPadding(0);
+            AGGroupBox->setIcon(QIcon(":/res/graphicsicon.png"));
+            //AGGroupBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+            AGGroupBox->setTitle(QGroupBox::tr("Audio/Graphic options"));
+
+            QVBoxLayout * GBAlayout = new QVBoxLayout(AGGroupBox);
+            QHBoxLayout * GBAreslayout = new QHBoxLayout(0);
+            QHBoxLayout * GBAstereolayout = new QHBoxLayout(0);
+            QHBoxLayout * GBAqualayout = new QHBoxLayout(0);
 
-        // Label and field for net nick
-        labelNN = new QLabel(groupMisc);
-        labelNN->setText(QLabel::tr("Nickname"));
-        MiscLayout->addWidget(labelNN, 1, 0);
+            CBFrontendFullscreen = new QCheckBox(AGGroupBox);
+            CBFrontendFullscreen->setText(QCheckBox::tr("Frontend fullscreen"));
+            GBAlayout->addWidget(CBFrontendFullscreen);
+
+            CBFrontendEffects = new QCheckBox(AGGroupBox);
+            CBFrontendEffects->setText(QCheckBox::tr("Frontend effects"));
+            GBAlayout->addWidget(CBFrontendEffects);
+
+            CBEnableFrontendSound = new QCheckBox(AGGroupBox);
+            CBEnableFrontendSound->setText(QCheckBox::tr("Enable frontend sounds"));
+            GBAlayout->addWidget(CBEnableFrontendSound);
+
+            CBEnableFrontendMusic = new QCheckBox(AGGroupBox);
+            CBEnableFrontendMusic->setText(QCheckBox::tr("Enable frontend music"));
+            GBAlayout->addWidget(CBEnableFrontendMusic);
 
-        editNetNick = new QLineEdit(groupMisc);
-        editNetNick->setMaxLength(20);
-        editNetNick->setText(QLineEdit::tr("anonymous"));
-        MiscLayout->addWidget(editNetNick, 1, 1);
+            QFrame * hr = new QFrame(AGGroupBox);
+            hr->setFrameStyle(QFrame::HLine);
+            hr->setLineWidth(3);
+            hr->setFixedHeight(10);
+            GBAlayout->addWidget(hr);
+
+            QLabel * resolution = new QLabel(AGGroupBox);
+            resolution->setText(QLabel::tr("Resolution"));
+            GBAreslayout->addWidget(resolution);
+
+            CBResolution = new QComboBox(AGGroupBox);
+            GBAreslayout->addWidget(CBResolution);
+            GBAlayout->addLayout(GBAreslayout);
 
-        // checkbox and field for password
-        CBSavePassword = new QCheckBox(groupMisc);
-        CBSavePassword->setText(QCheckBox::tr("Save password"));
-        MiscLayout->addWidget(CBSavePassword, 2, 0);
+            CBFullscreen = new QCheckBox(AGGroupBox);
+            CBFullscreen->setText(QCheckBox::tr("Fullscreen"));
+            GBAreslayout->addWidget(CBFullscreen);
+
+            QLabel * quality = new QLabel(AGGroupBox);
+            quality->setText(QLabel::tr("Quality"));
+            quality->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+            GBAqualayout->addWidget(quality);
+
+            SLQuality = new QSlider(Qt::Horizontal, AGGroupBox);
+            SLQuality->setTickPosition(QSlider::TicksBelow);
+            SLQuality->setMaximum(5);
+            SLQuality->setMinimum(0);
+            SLQuality->setFixedWidth(150);
+            GBAqualayout->addWidget(SLQuality);
+            GBAlayout->addLayout(GBAqualayout);
+
+            QLabel * stereo = new QLabel(AGGroupBox);
+            stereo->setText(QLabel::tr("Stereo rendering"));
+            GBAstereolayout->addWidget(stereo);
 
-        editNetPassword = new QLineEdit(groupMisc);
-        editNetPassword->setEchoMode(QLineEdit::Password);
-        MiscLayout->addWidget(editNetPassword, 2, 1);
+            CBStereoMode = new QComboBox(AGGroupBox);
+            CBStereoMode->addItem(QComboBox::tr("Disabled"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Cyan"));
+            CBStereoMode->addItem(QComboBox::tr("Cyan/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Blue"));
+            CBStereoMode->addItem(QComboBox::tr("Blue/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Green"));
+            CBStereoMode->addItem(QComboBox::tr("Green/Red"));
+            CBStereoMode->addItem(QComboBox::tr("Side-by-side"));
+            CBStereoMode->addItem(QComboBox::tr("Top-Bottom"));
+            CBStereoMode->addItem(QComboBox::tr("Wiggle"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Cyan grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Cyan/Red grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Blue grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Blue/Red grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Red/Green grayscale"));
+            CBStereoMode->addItem(QComboBox::tr("Green/Red grayscale"));
 
-        CBNameWithDate = new QCheckBox(groupMisc);
-        CBNameWithDate->setText(QCheckBox::tr("Append date and time to record file name"));
-        MiscLayout->addWidget(CBNameWithDate, 5, 0, 1, 2);
+            GBAstereolayout->addWidget(CBStereoMode);
+            GBAlayout->addLayout(GBAstereolayout);
+
+            QHBoxLayout * GBAfpslayout = new QHBoxLayout(0);
+            QLabel * maxfps = new QLabel(AGGroupBox);
+            maxfps->setText(QLabel::tr("FPS limit"));
+            GBAfpslayout->addWidget(maxfps);
+            GBAlayout->addLayout(GBAfpslayout);
+            fpsedit = new FPSEdit(AGGroupBox);
+            GBAfpslayout->addWidget(fpsedit);
+
+            CBShowFPS = new QCheckBox(AGGroupBox);
+            CBShowFPS->setText(QCheckBox::tr("Show FPS"));
+            GBAfpslayout->addWidget(CBShowFPS);
 
-        BtnAssociateFiles = new QPushButton(groupMisc);
-        BtnAssociateFiles->setText(QPushButton::tr("Associate file extensions"));
-        BtnAssociateFiles->setVisible(!custom_data && !custom_config);
-        MiscLayout->addWidget(BtnAssociateFiles, 6, 0, 1, 2);
+            hr = new QFrame(AGGroupBox);
+            hr->setFrameStyle(QFrame::HLine);
+            hr->setLineWidth(3);
+            hr->setFixedHeight(10);
+            GBAlayout->addWidget(hr);
+
+            QGridLayout * GBAvollayout = new QGridLayout();
+            QLabel * vol = new QLabel(AGGroupBox);
+            vol->setText(QLabel::tr("Initial sound volume"));
+            GBAvollayout->addWidget(vol, 0, 0, 1, 2);
+            GBAlayout->addLayout(GBAvollayout);
+            volumeBox = new QSpinBox(AGGroupBox);
+            volumeBox->setRange(0, 100);
+            volumeBox->setSingleStep(5);
+            GBAvollayout->addWidget(volumeBox, 0, 2);
 
-#ifdef __APPLE__
-#ifdef SPARKLE_ENABLED
-        CBAutoUpdate = new QCheckBox(groupMisc);
-        CBAutoUpdate->setText(QCheckBox::tr("Check for updates at startup"));
-        MiscLayout->addWidget(CBAutoUpdate, 7, 0, 1, 3);
-#endif
-#endif
-        gbTBLayout->addWidget(groupMisc, 2, 0);
+            CBEnableSound = new QCheckBox(AGGroupBox);
+            CBEnableSound->setText(QCheckBox::tr("Enable sound"));
+            GBAvollayout->addWidget(CBEnableSound, 1, 0, 1, 1);
+
+            CBEnableMusic = new QCheckBox(AGGroupBox);
+            CBEnableMusic->setText(QCheckBox::tr("Enable music"));
+            GBAvollayout->addWidget(CBEnableMusic, 1, 1, 1, 2);
+
+            GBAvollayout->setSizeConstraint(QLayout::SetMinimumSize);
+
+            hr = new QFrame(AGGroupBox);
+            hr->setFrameStyle(QFrame::HLine);
+            hr->setLineWidth(3);
+            hr->setFixedHeight(10);
+            GBAlayout->addWidget(hr);
+
+            CBAltDamage = new QCheckBox(AGGroupBox);
+            CBAltDamage->setText(QCheckBox::tr("Alternative damage show"));
+            GBAlayout->addWidget(CBAltDamage);
+
+            page1Layout->addWidget(AGGroupBox, 0, 1, 3, 1);
+        }
     }
 
-    {
-        AGGroupBox = new IconedGroupBox(this);
-        //AGGroupBox->setContentTopPadding(0);
-        AGGroupBox->setIcon(QIcon(":/res/graphicsicon.png"));
-        //AGGroupBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
-        AGGroupBox->setTitle(QGroupBox::tr("Audio/Graphic options"));
-
-        QVBoxLayout * GBAlayout = new QVBoxLayout(AGGroupBox);
-        QHBoxLayout * GBAreslayout = new QHBoxLayout(0);
-        QHBoxLayout * GBAstereolayout = new QHBoxLayout(0);
-        QHBoxLayout * GBAqualayout = new QHBoxLayout(0);
-
-        CBFrontendFullscreen = new QCheckBox(AGGroupBox);
-        CBFrontendFullscreen->setText(QCheckBox::tr("Frontend fullscreen"));
-        GBAlayout->addWidget(CBFrontendFullscreen);
-
-        CBFrontendEffects = new QCheckBox(AGGroupBox);
-        CBFrontendEffects->setText(QCheckBox::tr("Frontend effects"));
-        GBAlayout->addWidget(CBFrontendEffects);
-
-        CBEnableFrontendSound = new QCheckBox(AGGroupBox);
-        CBEnableFrontendSound->setText(QCheckBox::tr("Enable frontend sounds"));
-        GBAlayout->addWidget(CBEnableFrontendSound);
-
-        CBEnableFrontendMusic = new QCheckBox(AGGroupBox);
-        CBEnableFrontendMusic->setText(QCheckBox::tr("Enable frontend music"));
-        GBAlayout->addWidget(CBEnableFrontendMusic);
+    { // page 2
+        QGridLayout * page2Layout = new QGridLayout(page2);
 
-        QFrame * hr = new QFrame(AGGroupBox);
-        hr->setFrameStyle(QFrame::HLine);
-        hr->setLineWidth(3);
-        hr->setFixedHeight(10);
-        GBAlayout->addWidget(hr);
-
-        QLabel * resolution = new QLabel(AGGroupBox);
-        resolution->setText(QLabel::tr("Resolution"));
-        GBAreslayout->addWidget(resolution);
-
-        CBResolution = new QComboBox(AGGroupBox);
-        GBAreslayout->addWidget(CBResolution);
-        GBAlayout->addLayout(GBAreslayout);
+        IconedGroupBox * gbColors = new IconedGroupBox(this);
+        //gbColors->setIcon(QIcon(":/res/teamicon.png"));
+        gbColors->setTitle(QGroupBox::tr("Custom colors"));
+        page2Layout->addWidget(gbColors, 0, 0, 1, 3);
+        QVBoxLayout * gbCLayout = new QVBoxLayout(gbColors);
 
-        CBFullscreen = new QCheckBox(AGGroupBox);
-        CBFullscreen->setText(QCheckBox::tr("Fullscreen"));
-        GBAreslayout->addWidget(CBFullscreen);
-
-        QLabel * quality = new QLabel(AGGroupBox);
-        quality->setText(QLabel::tr("Quality"));
-        quality->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
-        GBAqualayout->addWidget(quality);
+        QSignalMapper * mapper = new QSignalMapper(this);
 
-        SLQuality = new QSlider(Qt::Horizontal, AGGroupBox);
-        SLQuality->setTickPosition(QSlider::TicksBelow);
-        SLQuality->setMaximum(5);
-        SLQuality->setMinimum(0);
-        SLQuality->setFixedWidth(150);
-        GBAqualayout->addWidget(SLQuality);
-        GBAlayout->addLayout(GBAqualayout);
-
-        QLabel * stereo = new QLabel(AGGroupBox);
-        stereo->setText(QLabel::tr("Stereo rendering"));
-        GBAstereolayout->addWidget(stereo);
+        QStandardItemModel * model = DataManager::instance().colorsModel();
 
-        CBStereoMode = new QComboBox(AGGroupBox);
-        CBStereoMode->addItem(QComboBox::tr("Disabled"));
-        CBStereoMode->addItem(QComboBox::tr("Red/Cyan"));
-        CBStereoMode->addItem(QComboBox::tr("Cyan/Red"));
-        CBStereoMode->addItem(QComboBox::tr("Red/Blue"));
-        CBStereoMode->addItem(QComboBox::tr("Blue/Red"));
-        CBStereoMode->addItem(QComboBox::tr("Red/Green"));
-        CBStereoMode->addItem(QComboBox::tr("Green/Red"));
-        CBStereoMode->addItem(QComboBox::tr("Side-by-side"));
-        CBStereoMode->addItem(QComboBox::tr("Top-Bottom"));
-        CBStereoMode->addItem(QComboBox::tr("Wiggle"));
-        CBStereoMode->addItem(QComboBox::tr("Red/Cyan grayscale"));
-        CBStereoMode->addItem(QComboBox::tr("Cyan/Red grayscale"));
-        CBStereoMode->addItem(QComboBox::tr("Red/Blue grayscale"));
-        CBStereoMode->addItem(QComboBox::tr("Blue/Red grayscale"));
-        CBStereoMode->addItem(QComboBox::tr("Red/Green grayscale"));
-        CBStereoMode->addItem(QComboBox::tr("Green/Red grayscale"));
-
-        GBAstereolayout->addWidget(CBStereoMode);
-        GBAlayout->addLayout(GBAstereolayout);
-
-        QHBoxLayout * GBAfpslayout = new QHBoxLayout(0);
-        QLabel * maxfps = new QLabel(AGGroupBox);
-        maxfps->setText(QLabel::tr("FPS limit"));
-        GBAfpslayout->addWidget(maxfps);
-        GBAlayout->addLayout(GBAfpslayout);
-        fpsedit = new FPSEdit(AGGroupBox);
-        GBAfpslayout->addWidget(fpsedit);
-
-        CBShowFPS = new QCheckBox(AGGroupBox);
-        CBShowFPS->setText(QCheckBox::tr("Show FPS"));
-        GBAfpslayout->addWidget(CBShowFPS);
+        connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(onColorModelDataChanged(QModelIndex,QModelIndex)));
+        for(int i = 0; i < model->rowCount(); ++i)
+        {
+            QPushButton * btn = new QPushButton(this);
+            gbCLayout->addWidget(btn);
+            btn->setStyleSheet(QString("background: %1").arg(model->item(i)->data().value<QColor>().name()));
+            m_colorButtons.append(btn);
+            connect(btn, SIGNAL(clicked()), mapper, SLOT(map()));
+            mapper->setMapping(btn, i);
+        }
 
-        hr = new QFrame(AGGroupBox);
-        hr->setFrameStyle(QFrame::HLine);
-        hr->setLineWidth(3);
-        hr->setFixedHeight(10);
-        GBAlayout->addWidget(hr);
-
-        QGridLayout * GBAvollayout = new QGridLayout();
-        QLabel * vol = new QLabel(AGGroupBox);
-        vol->setText(QLabel::tr("Initial sound volume"));
-        GBAvollayout->addWidget(vol, 0, 0, 1, 2);
-        GBAlayout->addLayout(GBAvollayout);
-        volumeBox = new QSpinBox(AGGroupBox);
-        volumeBox->setRange(0, 100);
-        volumeBox->setSingleStep(5);
-        GBAvollayout->addWidget(volumeBox, 0, 2);
-
-        CBEnableSound = new QCheckBox(AGGroupBox);
-        CBEnableSound->setText(QCheckBox::tr("Enable sound"));
-        GBAvollayout->addWidget(CBEnableSound, 1, 0, 1, 1);
-
-        CBEnableMusic = new QCheckBox(AGGroupBox);
-        CBEnableMusic->setText(QCheckBox::tr("Enable music"));
-        GBAvollayout->addWidget(CBEnableMusic, 1, 1, 1, 2);
-
-        GBAvollayout->setSizeConstraint(QLayout::SetMinimumSize);
-
-        hr = new QFrame(AGGroupBox);
-        hr->setFrameStyle(QFrame::HLine);
-        hr->setLineWidth(3);
-        hr->setFixedHeight(10);
-        GBAlayout->addWidget(hr);
-
-        CBAltDamage = new QCheckBox(AGGroupBox);
-        CBAltDamage->setText(QCheckBox::tr("Alternative damage show"));
-        GBAlayout->addWidget(CBAltDamage);
-
-        gbTBLayout->addWidget(AGGroupBox, 0, 1, 3, 1);
+        connect(mapper, SIGNAL(mapped(int)), this, SLOT(colorButtonClicked(int)));
     }
 
     previousQuality = this->SLQuality->value();
@@ -499,3 +528,25 @@
     CBTeamName->setVisible(enabled);
     LblNoEditTeam->setVisible(!enabled);
 }
+
+void PageOptions::colorButtonClicked(int i)
+{
+    if(i < 0 || i >= m_colorButtons.size())
+        return;
+
+    QPalette p = m_colorButtons[i]->palette();
+    QColor c = QColorDialog::getColor(p.color(QPalette::Button));
+
+    if(c.isValid())
+    {
+        DataManager::instance().colorsModel()->item(i)->setData(c);
+        m_colorButtons[i]->setStyleSheet(QString("background: %1").arg(c.name()));
+    }
+}
+
+void PageOptions::onColorModelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)
+{
+    QStandardItemModel * model = DataManager::instance().colorsModel();
+
+    m_colorButtons[topLeft.row()]->setStyleSheet(QString("background: %1").arg(model->item(topLeft.row())->data().value<QColor>().name()));
+}
--- a/QTfrontend/ui/page/pageoptions.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageoptions.h	Thu Aug 30 13:02:19 2012 -0400
@@ -23,6 +23,7 @@
 
 class FPSEdit;
 class IconedGroupBox;
+class QSignalMapper;
 
 class PageOptions : public AbstractPage
 {
@@ -91,6 +92,7 @@
         QPushButton *BtnNewTeam;
         QPushButton *BtnEditTeam;
         QPushButton *BtnDeleteTeam;
+        QList<QPushButton *> m_colorButtons;
 
     private slots:
         void forceFullscreen(int index);
@@ -101,6 +103,8 @@
         void requestEditSelectedTeam();
         void requestDeleteSelectedTeam();
         void savePwdChanged(int state);
+        void colorButtonClicked(int i);
+        void onColorModelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
 };
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/page/pagevideos.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,1136 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QGridLayout>
+#include <QPushButton>
+#include <QGroupBox>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QLabel>
+#include <QLineEdit>
+#include <QSpinBox>
+#include <QTableWidget>
+#include <QDir>
+#include <QProgressBar>
+#include <QStringList>
+#include <QDesktopServices>
+#include <QUrl>
+#include <QList>
+#include <QMessageBox>
+#include <QHeaderView>
+#include <QKeyEvent>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QFileSystemWatcher>
+#include <QDateTime>
+#include <QRegExp>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QXmlStreamReader>
+
+#include "hwconsts.h"
+#include "pagevideos.h"
+#include "igbox.h"
+#include "libav_iteraction.h"
+#include "gameuiconfig.h"
+#include "recorder.h"
+#include "ask_quit.h"
+#include "upload_video.h"
+
+static const QSize ThumbnailSize(350, 350*3/5);
+
+// columns in table with list of video files
+enum VideosColumns
+{
+    vcName,
+    vcSize,
+    vcProgress, // either encoding or uploading
+
+    vcNumColumns,
+};
+
+// this class is used for items in first column in file-table
+class VideoItem : public QTableWidgetItem
+{
+    // note: QTableWidgetItem is not Q_OBJECT
+
+    public:
+        VideoItem(const QString& name);
+        ~VideoItem();
+
+        QString name;
+        QString prefix; // original filename without extension
+        QString desc;   // description (duration, resolution, etc...)
+        QString uploadUrl; // http://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;
+
+        bool ready()
+        { return !pRecorder; }
+
+        QString path()
+        { return cfgdir->absoluteFilePath("Videos/" + name);  }
+};
+
+VideoItem::VideoItem(const QString& name)
+    : QTableWidgetItem(name, UserType)
+{
+    this->name = name;
+    pRecorder = NULL;
+    pUploading = NULL;
+    lastSizeUpdate = 0;
+    progress = 0;
+}
+
+VideoItem::~VideoItem()
+{}
+
+QLayout * PageVideos::bodyLayoutDefinition()
+{
+    QGridLayout * pPageLayout = new QGridLayout();
+    pPageLayout->setColumnStretch(0, 1);
+    pPageLayout->setColumnStretch(1, 2);
+    pPageLayout->setRowStretch(0, 1);
+    pPageLayout->setRowStretch(1, 1);
+
+    // options
+    {
+        IconedGroupBox* pOptionsGroup = new IconedGroupBox(this);
+        pOptionsGroup->setIcon(QIcon(":/res/Settings.png")); // FIXME
+        pOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+        pOptionsGroup->setTitle(QGroupBox::tr("Video recording options"));
+        QGridLayout * pOptLayout = new QGridLayout(pOptionsGroup);
+
+        // label for format
+        QLabel *labelFormat = new QLabel(pOptionsGroup);
+        labelFormat->setText(QLabel::tr("Format"));
+        pOptLayout->addWidget(labelFormat, 0, 0);
+
+        // list of supported formats
+        comboAVFormats = new QComboBox(pOptionsGroup);
+        pOptLayout->addWidget(comboAVFormats, 0, 1, 1, 4);
+        LibavIteraction::instance().fillFormats(comboAVFormats);
+
+        // separator
+        QFrame * hr = new QFrame(pOptionsGroup);
+        hr->setFrameStyle(QFrame::HLine);
+        hr->setLineWidth(3);
+        hr->setFixedHeight(10);
+        pOptLayout->addWidget(hr, 1, 0, 1, 5);
+
+        // label for audio codec
+        QLabel *labelACodec = new QLabel(pOptionsGroup);
+        labelACodec->setText(QLabel::tr("Audio codec"));
+        pOptLayout->addWidget(labelACodec, 2, 0);
+
+        // list of supported audio codecs
+        comboAudioCodecs = new QComboBox(pOptionsGroup);
+        pOptLayout->addWidget(comboAudioCodecs, 2, 1, 1, 3);
+
+        // checkbox 'record audio'
+        checkRecordAudio = new QCheckBox(pOptionsGroup);
+        checkRecordAudio->setText(QCheckBox::tr("Record audio"));
+        pOptLayout->addWidget(checkRecordAudio, 2, 4);
+
+        // separator
+        hr = new QFrame(pOptionsGroup);
+        hr->setFrameStyle(QFrame::HLine);
+        hr->setLineWidth(3);
+        hr->setFixedHeight(10);
+        pOptLayout->addWidget(hr, 3, 0, 1, 5);
+
+        // label for video codec
+        QLabel *labelVCodec = new QLabel(pOptionsGroup);
+        labelVCodec->setText(QLabel::tr("Video codec"));
+        pOptLayout->addWidget(labelVCodec, 4, 0);
+
+        // list of supported video codecs
+        comboVideoCodecs = new QComboBox(pOptionsGroup);
+        pOptLayout->addWidget(comboVideoCodecs, 4, 1, 1, 4);
+
+        // label for resolution
+        QLabel *labelRes = new QLabel(pOptionsGroup);
+        labelRes->setText(QLabel::tr("Resolution"));
+        pOptLayout->addWidget(labelRes, 5, 0);
+
+        // width
+        widthEdit = new QLineEdit(pOptionsGroup);
+        widthEdit->setValidator(new QIntValidator(this));
+        pOptLayout->addWidget(widthEdit, 5, 1);
+
+        // x
+        QLabel *labelX = new QLabel(pOptionsGroup);
+        labelX->setText("X");
+        pOptLayout->addWidget(labelX, 5, 2);
+
+        // height
+        heightEdit = new QLineEdit(pOptionsGroup);
+        heightEdit->setValidator(new QIntValidator(pOptionsGroup));
+        pOptLayout->addWidget(heightEdit, 5, 3);
+
+        // checkbox 'use game resolution'
+        checkUseGameRes = new QCheckBox(pOptionsGroup);
+        checkUseGameRes->setText(QCheckBox::tr("Use game resolution"));
+        pOptLayout->addWidget(checkUseGameRes, 5, 4);
+
+        // label for framerate
+        QLabel *labelFramerate = new QLabel(pOptionsGroup);
+        labelFramerate->setText(QLabel::tr("Framerate"));
+        pOptLayout->addWidget(labelFramerate, 6, 0);
+
+        // framerate
+        framerateBox = new QSpinBox(pOptionsGroup);
+        framerateBox->setRange(1, 200);
+        framerateBox->setSingleStep(1);
+        pOptLayout->addWidget(framerateBox, 6, 1);
+
+        // button 'set default options'
+        btnDefaults = new QPushButton(pOptionsGroup);
+        btnDefaults->setText(QPushButton::tr("Set default options"));
+        pOptLayout->addWidget(btnDefaults, 7, 0, 1, 5);
+
+        pPageLayout->addWidget(pOptionsGroup, 1, 0);
+    }
+
+    // list of videos
+    {
+        IconedGroupBox* pTableGroup = new IconedGroupBox(this);
+        pTableGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME
+        pTableGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+        pTableGroup->setTitle(QGroupBox::tr("Videos"));
+
+        QStringList columns;
+        columns << tr("Name");
+        columns << tr("Size");
+        columns << "";
+
+        filesTable = new QTableWidget(pTableGroup);
+        filesTable->setColumnCount(vcNumColumns);
+        filesTable->setHorizontalHeaderLabels(columns);
+        filesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+        filesTable->setSelectionMode(QAbstractItemView::SingleSelection);
+        filesTable->setEditTriggers(QAbstractItemView::SelectedClicked);
+        filesTable->verticalHeader()->hide();
+        filesTable->setMinimumWidth(400);
+
+        QHeaderView * header = filesTable->horizontalHeader();
+        header->setResizeMode(vcName, QHeaderView::ResizeToContents);
+        header->setResizeMode(vcSize, QHeaderView::Fixed);
+        header->resizeSection(vcSize, 100);
+        header->setStretchLastSection(true);
+
+        btnOpenDir = new QPushButton(QPushButton::tr("Open videos directory"), pTableGroup);
+
+        QVBoxLayout *box = new QVBoxLayout(pTableGroup);
+        box->addWidget(filesTable);
+        box->addWidget(btnOpenDir);
+
+        pPageLayout->addWidget(pTableGroup, 0, 1, 2, 1);
+    }
+
+    // description
+    {
+        IconedGroupBox* pDescGroup = new IconedGroupBox(this);
+        pDescGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME
+        pDescGroup->setTitle(QGroupBox::tr("Description"));
+
+        QVBoxLayout* pDescLayout = new QVBoxLayout(pDescGroup);
+        QHBoxLayout* pTopDescLayout = new QHBoxLayout(0);    // picture and text
+        QHBoxLayout* pBottomDescLayout = new QHBoxLayout(0); // buttons
+
+        // label with thumbnail picture
+        labelThumbnail = new QLabel(pDescGroup);
+        labelThumbnail->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
+        labelThumbnail->setMaximumSize(ThumbnailSize);
+        labelThumbnail->setStyleSheet(
+                    "QFrame {"
+                    "border: solid;"
+                    "border-width: 3px;"
+                    "border-color: #ffcc00;"
+                    "border-radius: 4px;"
+                    "}" );
+        clearThumbnail();
+        pTopDescLayout->addWidget(labelThumbnail, 2);
+
+        // label with file description
+        labelDesc = new QLabel(pDescGroup);
+        labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+        labelDesc->setTextInteractionFlags(Qt::TextSelectableByMouse |
+                                           Qt::TextSelectableByKeyboard	|
+                                           Qt::LinksAccessibleByMouse |
+                                           Qt::LinksAccessibleByKeyboard);
+        labelDesc->setTextFormat(Qt::RichText);
+        labelDesc->setOpenExternalLinks(true);
+        pTopDescLayout->addWidget(labelDesc, 1);
+
+        // buttons: play and delete
+        btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
+        btnPlay->setEnabled(false);
+        pBottomDescLayout->addWidget(btnPlay);
+        btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
+        btnDelete->setEnabled(false);
+        pBottomDescLayout->addWidget(btnDelete);
+        btnToYouTube = new QPushButton(QPushButton::tr("Upload to YouTube"), pDescGroup);
+        btnToYouTube->setEnabled(false);
+        pBottomDescLayout->addWidget(btnToYouTube);
+
+        pDescLayout->addStretch(1);
+        pDescLayout->addLayout(pTopDescLayout, 0);
+        pDescLayout->addStretch(1);
+        pDescLayout->addLayout(pBottomDescLayout, 0);
+
+        pPageLayout->addWidget(pDescGroup, 0, 0);
+    }
+
+    return pPageLayout;
+}
+
+QLayout * PageVideos::footerLayoutDefinition()
+{
+    return NULL;
+}
+
+void PageVideos::connectSignals()
+{
+    connect(checkUseGameRes, SIGNAL(stateChanged(int)), this, SLOT(changeUseGameRes(int)));
+    connect(checkRecordAudio, SIGNAL(stateChanged(int)), this, SLOT(changeRecordAudio(int)));
+    connect(comboAVFormats, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAVFormat(int)));
+    connect(btnDefaults, SIGNAL(clicked()), this, SLOT(setDefaultOptions()));
+    connect(filesTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellDoubleClicked(int, int)));
+    connect(filesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int, int)));
+    connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged(int,int,int,int)));
+    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)
+{
+    nameChangedFromCode = false;
+    numRecorders = 0;
+    numUploads = 0;
+    initPage();
+}
+
+void PageVideos::init(GameUIConfig * config)
+{
+    this->config = config;
+
+    QString path = cfgdir->absolutePath() + "/Videos";
+    QFileSystemWatcher * pWatcher = new QFileSystemWatcher(this);
+    pWatcher->addPath(path);
+    connect(pWatcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(updateFileList(const QString &)));
+    updateFileList(path);
+
+    startEncoding(); // this is for videos recorded from demos which were executed directly (without frontend)
+}
+
+// user changed file format, we need to update list of codecs
+void PageVideos::changeAVFormat(int index)
+{
+    // remember selected codecs
+    QString prevVCodec = videoCodec();
+    QString prevACodec = audioCodec();
+
+    // clear lists of codecs
+    comboVideoCodecs->clear();
+    comboAudioCodecs->clear();
+
+    // get list of codecs for specified format
+    LibavIteraction::instance().fillCodecs(comboAVFormats->itemData(index).toString(), comboVideoCodecs, comboAudioCodecs);
+
+    // disable audio if there is no audio codec
+    if (comboAudioCodecs->count() == 0)
+    {
+        checkRecordAudio->setChecked(false);
+        checkRecordAudio->setEnabled(false);
+    }
+    else
+        checkRecordAudio->setEnabled(true);
+
+    // restore selected codecs if possible
+    int iVCodec = comboVideoCodecs->findData(prevVCodec);
+    if (iVCodec != -1)
+        comboVideoCodecs->setCurrentIndex(iVCodec);
+    int iACodec = comboAudioCodecs->findData(prevACodec);
+    if (iACodec != -1)
+        comboAudioCodecs->setCurrentIndex(iACodec);
+}
+
+// user switched checkbox 'use game resolution'
+void PageVideos::changeUseGameRes(int state)
+{
+    if (state && config)
+    {
+        // set resolution to game resolution
+        QRect resolution = config->vid_Resolution();
+        widthEdit->setText(QString::number(resolution.width()));
+        heightEdit->setText(QString::number(resolution.height()));
+    }
+    widthEdit->setEnabled(!state);
+    heightEdit->setEnabled(!state);
+}
+
+// user switched checkbox 'record audio'
+void PageVideos::changeRecordAudio(int state)
+{
+    comboAudioCodecs->setEnabled(!!state);
+}
+
+void PageVideos::setDefaultCodecs()
+{
+    if (tryCodecs("mp4", "libx264", "libmp3lame"))
+        return;
+    if (tryCodecs("mp4", "libx264", "libfaac"))
+        return;
+    if (tryCodecs("mp4", "libx264", "libvo_aacenc"))
+        return;
+    if (tryCodecs("mp4", "libx264", "aac"))
+        return;
+    if (tryCodecs("mp4", "libx264", "mp2"))
+        return;
+    if (tryCodecs("avi", "libxvid", "libmp3lame"))
+        return;
+    if (tryCodecs("avi", "libxvid", "ac3_fixed"))
+        return;
+    if (tryCodecs("avi", "libxvid", "mp2"))
+        return;
+    if (tryCodecs("avi", "mpeg4", "libmp3lame"))
+        return;
+    if (tryCodecs("avi", "mpeg4", "ac3_fixed"))
+        return;
+    if (tryCodecs("avi", "mpeg4", "mp2"))
+        return;
+
+    // this shouldn't happen, just in case
+    if (tryCodecs("ogg", "libtheora", "libvorbis"))
+        return;
+    tryCodecs("ogg", "libtheora", "flac");
+}
+
+void PageVideos::setDefaultOptions()
+{
+    framerateBox->setValue(25);
+    checkRecordAudio->setChecked(true);
+    checkUseGameRes->setChecked(true);
+    setDefaultCodecs();
+}
+
+bool PageVideos::tryCodecs(const QString & format, const QString & vcodec, const QString & acodec)
+{
+    // first we should change format
+    int iFormat = comboAVFormats->findData(format);
+    if (iFormat == -1)
+        return false;
+    comboAVFormats->setCurrentIndex(iFormat);
+    // format was changed, so lists of codecs were automatically updated to codecs supported by this format
+
+    // try to find video codec
+    int iVCodec = comboVideoCodecs->findData(vcodec);
+    if (iVCodec == -1)
+        return false;
+    comboVideoCodecs->setCurrentIndex(iVCodec);
+
+    // try to find audio codec
+    int iACodec = comboAudioCodecs->findData(acodec);
+    if (iACodec == -1 && checkRecordAudio->isChecked())
+        return false;
+    if (iACodec != -1)
+        comboAudioCodecs->setCurrentIndex(iACodec);
+
+    return true;
+}
+
+// get file size as string
+static QString FileSizeStr(const QString & path)
+{
+    quint64 size = QFileInfo(path).size();
+
+    quint64 KiB = 1024;
+    quint64 MiB = 1024*KiB;
+    quint64 GiB = 1024*MiB;
+    QString sizeStr;
+    if (size >= GiB)
+        return QString("%1 GiB").arg(QString::number(float(size)/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 PageVideos::tr("%1 bytes").arg(QString::number(size));
+}
+
+// set file size in file list in specified row
+void PageVideos::updateSize(int row)
+{
+    VideoItem * item = nameItem(row);
+    QString path = item->ready()? item->path() : cfgdir->absoluteFilePath("VideoTemp/" + item->pRecorder->name);
+    filesTable->item(row, vcSize)->setText(FileSizeStr(path));
+}
+
+// There is a button 'Open videos dir', so it is possible that user will open
+// this dir and rename/delete some files there, so we should handle this.
+void PageVideos::updateFileList(const QString & path)
+{
+    // mark all files as non seen
+    int numRows = filesTable->rowCount();
+    for (int i = 0; i < numRows; i++)
+        nameItem(i)->seen = false;
+
+    QStringList files = QDir(path).entryList(QDir::Files);
+    foreach (const QString & name, files)
+    {
+        int row = -1;
+        foreach (QTableWidgetItem * item, filesTable->findItems(name, Qt::MatchExactly))
+        {
+            if (item->type() != QTableWidgetItem::UserType || !((VideoItem*)item)->ready())
+                continue;
+            row = item->row();
+            break;
+        }
+        if (row == -1)
+            row = appendRow(name);
+        VideoItem * item = nameItem(row);
+        item->seen = true;
+        item->desc = "";
+        updateSize(row);
+    }
+
+    // remove all non seen files
+    for (int i = 0; i < filesTable->rowCount();)
+    {
+        VideoItem * item = nameItem(i);
+        if (item->ready() && !item->seen)
+            filesTable->removeRow(i);
+        else
+            i++;
+    }
+}
+
+void PageVideos::addRecorder(HWRecorder* pRecorder)
+{
+    int row = appendRow(pRecorder->name);
+    VideoItem * item = nameItem(row);
+    item->pRecorder = pRecorder;
+    pRecorder->item = item;
+
+    // add progress bar
+    QProgressBar * progressBar = new QProgressBar(filesTable);
+    progressBar->setMinimum(0);
+    progressBar->setMaximum(10000);
+    progressBar->setValue(0);
+    connect(pRecorder, SIGNAL(onProgress(float)), this, SLOT(updateProgress(float)));
+    connect(pRecorder, SIGNAL(encodingFinished(bool)), this, SLOT(encodingFinished(bool)));
+    filesTable->setCellWidget(row, vcProgress, progressBar);
+
+    numRecorders++;
+}
+
+void PageVideos::setProgress(int row, VideoItem* item, float value)
+{
+    QProgressBar * progressBar = (QProgressBar*)filesTable->cellWidget(row, vcProgress);
+    progressBar->setValue(value*10000);
+    progressBar->setFormat(QString("%1%").arg(value*100, 0, 'f', 2));
+    item->progress = value;
+}
+
+void PageVideos::updateProgress(float value)
+{
+    HWRecorder * pRecorder = (HWRecorder*)sender();
+    VideoItem * item = pRecorder->item;
+    int row = filesTable->row(item);
+
+    // update file size every percent
+    if (value - item->lastSizeUpdate > 0.01)
+    {
+        updateSize(row);
+        item->lastSizeUpdate = value;
+    }
+
+    setProgress(row, item, value);
+}
+
+void PageVideos::encodingFinished(bool success)
+{
+    numRecorders--;
+
+    HWRecorder * pRecorder = (HWRecorder*)sender();
+    VideoItem * item = (VideoItem*)pRecorder->item;
+    int row = filesTable->row(item);
+
+    if (success)
+    {
+        // move file to destination
+        success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + item->name);
+        if (!success)
+        {
+            // unable to rename for some reason (maybe user entered incorrect name);
+            // try to use temp name instead.
+            success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + pRecorder->name);
+            if (success)
+                setName(item, pRecorder->name);
+        }
+    }
+
+    if (!success)
+    {
+        filesTable->removeRow(row);
+        return;
+    }
+
+    filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
+    item->pRecorder = NULL;
+    updateSize(row);
+    updateDescription();
+}
+
+void PageVideos::cellDoubleClicked(int row, int column)
+{
+    play(row);
+}
+
+void PageVideos::cellChanged(int row, int column)
+{
+    // user can only edit name
+    if (column != vcName || nameChangedFromCode)
+        return;
+
+    // user has edited filename, so we should rename the file
+    VideoItem * item = nameItem(row);
+    QString oldName = item->name;
+    QString newName = item->text();
+    if (!newName.contains('.')) // user forgot an extension
+    {
+        // restore old extension
+        int pt = oldName.lastIndexOf('.');
+        if (pt != -1)
+        {
+            newName += oldName.right(oldName.length() - pt);
+            setName(item, newName);
+        }
+    }
+#ifdef Q_WS_WIN
+    // there is a bug in qt, QDir::rename() doesn't fail on such names but damages files
+    if (newName.contains(QRegExp("[\"*:<>?\/|]")))
+    {
+        setName(item, oldName);
+        return;
+    }
+#endif
+    if (item->ready() && !cfgdir->rename("Videos/" + oldName, "Videos/" + newName))
+    {
+        // unable to rename for some reason (maybe user entered incorrect name),
+        // therefore restore old name in cell
+        setName(item, oldName);
+        return;
+    }
+    item->name = newName;
+    updateDescription();
+}
+
+void PageVideos::setName(VideoItem * item, const QString & newName)
+{
+    nameChangedFromCode = true;
+    item->setText(newName);
+    nameChangedFromCode = false;
+    item->name = newName;
+}
+
+int PageVideos::appendRow(const QString & name)
+{
+    int row = filesTable->rowCount();
+    filesTable->setRowCount(row+1);
+
+    // add 'name' item
+    QTableWidgetItem * item = new VideoItem(name);
+    item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+    nameChangedFromCode = true;
+    filesTable->setItem(row, vcName, item);
+    nameChangedFromCode = false;
+
+    // add 'size' item
+    item = new QTableWidgetItem();
+    item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+    item->setTextAlignment(Qt::AlignRight);
+    filesTable->setItem(row, vcSize, item);
+
+    // add 'progress' item
+    item = new QTableWidgetItem();
+    item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+    filesTable->setItem(row, vcProgress, item);
+
+    return row;
+}
+
+VideoItem* PageVideos::nameItem(int row)
+{
+    return (VideoItem*)filesTable->item(row, vcName);
+}
+
+void PageVideos::clearThumbnail()
+{
+    // add empty (transparent) image for proper sizing
+    QPixmap pic(ThumbnailSize);
+    pic.fill(QColor(0,0,0,0));
+    labelThumbnail->setPixmap(pic);
+}
+
+void PageVideos::updateDescription()
+{
+    VideoItem * item = nameItem(filesTable->currentRow());
+    if (!item)
+    {
+        // nothing is selected => clear description and return
+        labelDesc->clear();
+        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";
+
+    if (!item->ready())
+        desc += tr("(in progress...)");
+    else
+    {
+        QString path = item->path();
+        desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + '\n';
+        desc += tr("Size: ") + FileSizeStr(path) + '\n';
+        if (item->desc.isEmpty())
+        {
+            // Extract description from file;
+            // It will contain duration, resolution, etc and also comment added by hwengine.
+            item->desc = LibavIteraction::instance().getFileInfo(path);
+
+            // extract prefix (original name) from description (it is enclosed in prefix[???]prefix)
+            int prefixBegin = item->desc.indexOf("prefix[");
+            int prefixEnd   = item->desc.indexOf("]prefix");
+            if (prefixBegin != -1 && prefixEnd != -1)
+            {
+                item->prefix = item->desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7));
+                item->desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin);
+            }
+        }
+        desc += item->desc + '\n';
+    }
+
+    if (item->prefix.isEmpty())
+    {
+        // try to extract prefix from file name instead
+        if (item->ready())
+            item->prefix = item->name;
+        else
+            item->prefix = item->pRecorder->name;
+
+        // remove extension
+        int pt = item->prefix.lastIndexOf('.');
+        if (pt != -1)
+            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);
+
+    if (!item->prefix.isEmpty())
+    {
+        QString thumbName = cfgdir->absoluteFilePath("VideoTemp/" + item->prefix);
+        QPixmap pic;
+        if (pic.load(thumbName + ".png") || pic.load(thumbName + ".bmp"))
+        {
+            if (pic.height()*ThumbnailSize.width() > pic.width()*ThumbnailSize.height())
+                pic = pic.scaledToWidth(ThumbnailSize.width());
+            else
+                pic = pic.scaledToHeight(ThumbnailSize.height());
+            labelThumbnail->setPixmap(pic);
+        }
+        else
+            clearThumbnail();
+    }
+}
+
+// user selected another cell, so we should change description
+void PageVideos::currentCellChanged(int row, int column, int previousRow, int previousColumn)
+{
+    updateDescription();
+}
+
+// open video file in external media player
+void PageVideos::play(int row)
+{
+    VideoItem * item = nameItem(row);
+    if (item && item->ready())
+        QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(item->path())));
+}
+
+void PageVideos::playSelectedFile()
+{
+    int index = filesTable->currentRow();
+    if (index != -1)
+        play(index);
+}
+
+void PageVideos::deleteSelectedFiles()
+{
+    int index = filesTable->currentRow();
+    if (index == -1)
+        return;
+
+    VideoItem * item = nameItem(index);
+    if (!item)
+        return;
+
+    // ask user if (s)he is serious
+    if (QMessageBox::question(this,
+                              tr("Are you sure?"),
+                              tr("Do you really want do remove %1?").arg(item->name),
+                              QMessageBox::Yes | QMessageBox::No)
+            != QMessageBox::Yes)
+        return;
+
+    // remove
+    if (!item->ready())
+        item->pRecorder->deleteLater();
+    else
+        cfgdir->remove("Videos/" + item->name);
+
+// this code is for removing several files when multiple selection is enabled
+#if 0
+    QList<QTableWidgetItem*> items = filesTable->selectedItems();
+    int num = items.size() / vcNumColumns;
+    if (num == 0)
+        return;
+
+    // ask user if (s)he is serious
+    if (QMessageBox::question(this,
+                              tr("Are you sure?"),
+                              tr("Do you really want do remove %1 file(s)?").arg(num),
+                              QMessageBox::Yes | QMessageBox::No)
+            != QMessageBox::Yes)
+        return;
+
+    // remove
+    foreach (QTableWidgetItem * witem, items)
+    {
+        if (witem->type() != QTableWidgetItem::UserType)
+            continue;
+        VideoItem * item = (VideoItem*)witem;
+        if (!item->ready())
+            item->pRecorder->deleteLater();
+        else
+            cfgdir->remove("Videos/" + item->name);
+    }
+#endif
+}
+
+void PageVideos::keyPressEvent(QKeyEvent * pEvent)
+{
+    if (filesTable->hasFocus())
+    {
+        if (pEvent->key() == Qt::Key_Delete)
+        {
+            deleteSelectedFiles();
+            return;
+        }
+        if (pEvent->key() == Qt::Key_Enter) // doesn't work
+        {
+            playSelectedFile();
+            return;
+        }
+    }
+    AbstractPage::keyPressEvent(pEvent);
+}
+
+void PageVideos::openVideosDirectory()
+{
+    QString path = QDir::toNativeSeparators(cfgdir->absolutePath() + "/Videos");
+    QDesktopServices::openUrl(QUrl("file:///" + path));
+}
+
+// clear VideoTemp directory (except for thumbnails and upload links)
+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"))
+            temp.remove(file);
+    }
+}
+
+bool PageVideos::tryQuit(HWForm * form)
+{
+    bool quit = true;
+    if (numRecorders != 0 || numUploads != 0)
+    {
+        // ask user what to do - abort or wait
+        HWAskQuitDialog * askd = new HWAskQuitDialog(this, form);
+        askd->deleteLater();
+        quit = askd->exec();
+    }
+    if (quit)
+        clearTemp();
+    return quit;
+}
+
+// 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()
+{
+    QString list = "";
+    int count = filesTable->rowCount();
+    for (int i = 0; i < count; i++)
+    {
+        VideoItem * item = nameItem(i);
+        QString process;
+        if (!item->ready())
+            process = tr("encoding");
+        else if (item->pUploading)
+            process = tr("uploading");
+        else
+            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";
+    }
+    return list;
+}
+
+void PageVideos::startEncoding(const QByteArray & record)
+{
+    QDir videoTempDir(cfgdir->absolutePath() + "/VideoTemp/");
+    QStringList files = videoTempDir.entryList(QStringList("*.txtout"), QDir::Files);
+    foreach (const QString & str, files)
+    {
+        QString prefix = str;
+        prefix.chop(7); // remove ".txtout"
+        videoTempDir.rename(prefix + ".txtout", prefix + ".txtin"); // rename this file to not open it twice
+
+        HWRecorder* pRecorder = new HWRecorder(config, prefix);
+
+        if (!record.isEmpty())
+            pRecorder->EncodeVideo(record);
+        else
+        {
+            // this is for videos recorded from demos which were executed directly (without frontend)
+            QFile demofile(videoTempDir.absoluteFilePath(prefix + ".hwd"));
+            if (!demofile.open(QIODevice::ReadOnly))
+                continue;
+            QByteArray demo = demofile.readAll();
+            if (demo.isEmpty())
+                continue;
+            pRecorder->EncodeVideo(demo);
+        }
+        addRecorder(pRecorder);
+    }
+}
+
+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 = "http://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::fromAscii(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)
+    {
+        if (QMessageBox::question(this,
+                                  tr("Are you sure?"),
+                                  tr("Do you really want do cancel uploading %1?").arg(item->name),
+                                  QMessageBox::Yes | QMessageBox::No)
+                != QMessageBox::Yes)
+            return;
+        item->pUploading->deleteLater();
+        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();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/page/pagevideos.h	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,125 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+#ifndef PAGE_VIDEOS_H
+#define PAGE_VIDEOS_H
+
+#include "AbstractPage.h"
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class GameUIConfig;
+class HWRecorder;
+class VideoItem;
+class HWForm;
+
+class PageVideos : public AbstractPage
+{
+        Q_OBJECT
+
+    public:
+        PageVideos(QWidget* parent = 0);
+
+        QSpinBox  *framerateBox;
+        QLineEdit *widthEdit;
+        QLineEdit *heightEdit;
+        QCheckBox *checkUseGameRes;
+        QCheckBox *checkRecordAudio;
+
+        QString format()
+        { return comboAVFormats->itemData(comboAVFormats->currentIndex()).toString(); }
+
+        QString videoCodec()
+        { return comboVideoCodecs->itemData(comboVideoCodecs->currentIndex()).toString(); }
+
+        QString audioCodec()
+        { return comboAudioCodecs->itemData(comboAudioCodecs->currentIndex()).toString(); }
+
+        void setDefaultCodecs();
+        bool tryCodecs(const QString & format, const QString & vcodec, const QString & acodec);
+        void addRecorder(HWRecorder* pRecorder);
+        bool tryQuit(HWForm *form);
+        QString getVideosInProgress(); // get multi-line string with list of videos in progress
+        void startEncoding(const QByteArray & record = QByteArray());
+        void init(GameUIConfig * config);
+
+    private:
+        // virtuals from AbstractPage
+        QLayout * bodyLayoutDefinition();
+        QLayout * footerLayoutDefinition();
+        void connectSignals();
+
+        // virtual from QWidget
+        void keyPressEvent(QKeyEvent * pEvent);
+
+        void setName(VideoItem * item, const QString & newName);
+        void updateSize(int row);
+        int appendRow(const QString & name);
+        VideoItem* nameItem(int row);
+        void play(int row);
+        void updateDescription();
+        void clearTemp();
+        void clearThumbnail();
+        void setProgress(int row, VideoItem* item, float value);
+        VideoItem * itemFromReply(QNetworkReply* reply, int & row);
+
+        GameUIConfig * config;
+        QNetworkAccessManager* netManager;
+
+        // options group
+        QComboBox *comboAVFormats;
+        QComboBox *comboVideoCodecs;
+        QComboBox *comboAudioCodecs;
+        QPushButton *btnDefaults;
+
+        // file list group
+        QTableWidget *filesTable;
+        QPushButton *btnOpenDir;
+
+        // description group
+        QPushButton *btnPlay, *btnDelete, *btnToYouTube;
+        QLabel *labelDesc;
+        QLabel *labelThumbnail;
+
+        // this flag is used to distinguish if cell was changed from code or by user
+        // (in signal cellChanged)
+        bool nameChangedFromCode;
+
+        int numRecorders, numUploads;
+
+    private slots:
+        void changeAVFormat(int index);
+        void changeUseGameRes(int state);
+        void changeRecordAudio(int state);
+        void setDefaultOptions();
+        void encodingFinished(bool success);
+        void updateProgress(float value);
+        void cellDoubleClicked(int row, int column);
+        void cellChanged(int row, int column);
+        void currentCellChanged(int row, int column, int previousRow, int previousColumn);
+        void playSelectedFile();
+        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/chatwidget.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/widget/chatwidget.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -922,7 +922,7 @@
     if(b)
     {
         chatNicks->insertAction(0, acKick);
-//      chatNicks->insertAction(0, acBan);
+        chatNicks->insertAction(0, acBan);
     }
 }
 
--- a/QTfrontend/ui/widget/teamselect.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/widget/teamselect.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -135,6 +135,11 @@
             curPlayingTeams.erase(itPlay);
             break;
         }
+        else
+        {
+            qWarning() << QString("removeNetTeam: team '%1' was actually a local team!").arg(team.name());
+            break;
+        }
     }
     emit setEnabledGameStart(curPlayingTeams.size()>1);
 }
--- a/QTfrontend/ui_hwform.cpp	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui_hwform.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -46,6 +46,7 @@
 #include "pagegamestats.h"
 #include "pageplayrecord.h"
 #include "pagedata.h"
+#include "pagevideos.h"
 #include "hwconsts.h"
 
 void Ui_HWForm::setupUi(HWForm *HWForm)
@@ -145,4 +146,7 @@
 
     pageFeedback = new PageFeedback();
     Pages->addWidget(pageFeedback);
+
+    pageVideos = new PageVideos();
+    Pages->addWidget(pageVideos);
 }
--- a/QTfrontend/ui_hwform.h	Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui_hwform.h	Thu Aug 30 13:02:19 2012 -0400
@@ -43,6 +43,7 @@
 class PageAdmin;
 class PageNetType;
 class PageDrawMap;
+class PageVideos;
 class QStackedLayout;
 class QFont;
 class QWidget;
@@ -78,6 +79,7 @@
         PageNetType *pageNetType;
         PageCampaign *pageCampaign;
         PageDrawMap *pageDrawMap;
+        PageVideos *pageVideos;
 
         QStackedLayout *Pages;
         QFont *font14;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/util/libav_iteraction.cpp	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,363 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "libav_iteraction.h"
+
+#if VIDEOREC
+#define __STDC_CONSTANT_MACROS
+extern "C"
+{
+#include "libavformat/avformat.h"
+}
+
+#include <QVector>
+#include <QList>
+#include <QMessageBox>
+#include <QComboBox>
+
+#include "HWApplication.h"
+
+struct Codec
+{
+    CodecID id;
+    bool isAudio;
+    QString shortName; // used for identification
+    QString longName; // used for displaying to user
+    bool isRecomended;
+};
+
+struct Format
+{
+    QString shortName;
+    QString longName;
+    bool isRecomended;
+    QString extension;
+    QVector<Codec*> codecs;
+};
+
+QList<Codec> codecs;
+QMap<QString,Format> formats;
+
+// test if given format supports given codec
+bool FormatQueryCodec(AVOutputFormat *ofmt, enum CodecID codec_id)
+{
+#if LIBAVFORMAT_VERSION_MAJOR >= 54
+    return avformat_query_codec(ofmt, codec_id, FF_COMPLIANCE_NORMAL) == 1;
+#else
+    if (ofmt->codec_tag)
+        return !!av_codec_get_tag(ofmt->codec_tag, codec_id);
+    return codec_id == ofmt->video_codec || codec_id == ofmt->audio_codec;
+#endif
+}
+
+LibavIteraction::LibavIteraction()
+{
+    // initialize libav and register all codecs and formats
+    av_register_all();
+
+    // get list of all codecs
+    AVCodec* pCodec = NULL;
+    while (pCodec = av_codec_next(pCodec))
+    {
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+        if (!av_codec_is_encoder(pCodec))
+#else
+        if (!pCodec->encode)
+#endif
+            continue;
+
+        if (pCodec->type != AVMEDIA_TYPE_VIDEO && pCodec->type != AVMEDIA_TYPE_AUDIO)
+            continue;
+
+        // this encoders seems to be buggy
+        if (strcmp(pCodec->name, "rv10") == 0 || strcmp(pCodec->name, "rv20") == 0)
+            continue;
+
+        // doesn't support stereo sound
+        if (strcmp(pCodec->name, "real_144") == 0)
+            continue;
+
+        if (!pCodec->long_name || strlen(pCodec->long_name) == 0)
+            continue;
+
+        if (pCodec->type == AVMEDIA_TYPE_VIDEO)
+        {
+            if (pCodec->supported_framerates != NULL)
+                continue;
+
+            // check if codec supports yuv 4:2:0 format
+            if (!pCodec->pix_fmts)
+                continue;
+            bool yuv420Supported = false;
+            for (const PixelFormat* pfmt = pCodec->pix_fmts; *pfmt != -1; pfmt++)
+                if (*pfmt == PIX_FMT_YUV420P)
+                {
+                    yuv420Supported = true;
+                    break;
+                }
+            if (!yuv420Supported)
+                continue;
+        }
+        if (pCodec->type == AVMEDIA_TYPE_AUDIO)
+        {
+            // check if codec supports signed 16-bit format
+            if (!pCodec->sample_fmts)
+                continue;
+            bool s16Supported = false;
+            for (const AVSampleFormat* pfmt = pCodec->sample_fmts; *pfmt != -1; pfmt++)
+                if (*pfmt == AV_SAMPLE_FMT_S16)
+                {
+                    s16Supported = true;
+                    break;
+                }
+            if (!s16Supported)
+                continue;
+        }
+        // add codec to list of codecs
+        codecs.push_back(Codec());
+        Codec & codec = codecs.back();
+        codec.id = pCodec->id;
+        codec.isAudio = pCodec->type == AVMEDIA_TYPE_AUDIO;
+        codec.shortName = pCodec->name;
+        codec.longName = pCodec->long_name;
+
+        codec.isRecomended = false;
+        if (strcmp(pCodec->name, "libx264") == 0)
+        {
+            codec.longName = "H.264/MPEG-4 Part 10 AVC (x264)";
+            codec.isRecomended = true;
+        }
+        else if (strcmp(pCodec->name, "libxvid") == 0)
+        {
+            codec.longName = "MPEG-4 Part 2 (Xvid)";
+            codec.isRecomended = true;
+        }
+        else if (strcmp(pCodec->name, "libmp3lame") == 0)
+        {
+            codec.longName = "MP3 (MPEG audio layer 3) (LAME)";
+            codec.isRecomended = true;
+        }
+        else
+            codec.longName = pCodec->long_name;
+
+        if (strcmp(pCodec->name, "mpeg4") == 0 || strcmp(pCodec->name, "ac3_fixed") == 0)
+            codec.isRecomended = true;
+
+        // FIXME: remove next line
+        //codec.longName += QString(" (%1)").arg(codec.shortName);
+    }
+
+    // get list of all formats
+    AVOutputFormat* pFormat = NULL;
+    while (pFormat = av_oformat_next(pFormat))
+    {
+        if (!pFormat->extensions)
+            continue;
+
+        // skip some strange formats to not confuse users
+        if (strstr(pFormat->long_name, "raw"))
+            continue;
+
+        Format format;
+        bool hasVideoCodec = false;
+        for (QList<Codec>::iterator codec = codecs.begin(); codec != codecs.end(); ++codec)
+        {
+            if (!FormatQueryCodec(pFormat, codec->id))
+                continue;
+            format.codecs.push_back(&*codec);
+            if (!codec->isAudio)
+                hasVideoCodec = true;
+        }
+        if (!hasVideoCodec)
+            continue;
+
+        QString ext(pFormat->extensions);
+        ext.truncate(strcspn(pFormat->extensions, ","));
+        format.extension = ext;
+        format.shortName = pFormat->name;
+        format.longName = QString("%1 (*.%2)").arg(pFormat->long_name).arg(ext);
+
+        // FIXME: remove next line
+        //format.longName += QString(" (%1)").arg(format.shortName);
+
+        format.isRecomended = strcmp(pFormat->name, "mp4") == 0 || strcmp(pFormat->name, "avi") == 0;
+
+        formats[pFormat->name] = format;
+    }
+}
+
+void LibavIteraction::fillFormats(QComboBox * pFormats)
+{
+    // first insert recomended formats
+    foreach(const Format & format, formats)
+        if (format.isRecomended)
+            pFormats->addItem(format.longName, format.shortName);
+
+    // remember where to place separator between recomended and other formats
+    int sep = pFormats->count();
+
+    // insert remaining formats
+    foreach(const Format & format, formats)
+        if (!format.isRecomended)
+            pFormats->addItem(format.longName, format.shortName);
+
+    // insert separator if necessary
+    if (sep != 0 && sep != pFormats->count())
+        pFormats->insertSeparator(sep);
+}
+
+void LibavIteraction::fillCodecs(const QString & fmt, QComboBox * pVCodecs, QComboBox * pACodecs)
+{
+    Format & format = formats[fmt];
+
+    // first insert recomended codecs
+    foreach(Codec * codec, format.codecs)
+    {
+        if (codec->isRecomended)
+        {
+            if (codec->isAudio)
+                pACodecs->addItem(codec->longName, codec->shortName);
+            else
+                pVCodecs->addItem(codec->longName, codec->shortName);
+        }
+    }
+
+    // remember where to place separators between recomended and other codecs
+    int vsep = pVCodecs->count();
+    int asep = pACodecs->count();
+
+    // insert remaining codecs
+    foreach(Codec * codec, format.codecs)
+    {
+        if (!codec->isRecomended)
+        {
+            if (codec->isAudio)
+                pACodecs->addItem(codec->longName, codec->shortName);
+            else
+                pVCodecs->addItem(codec->longName, codec->shortName);
+        }
+    }
+
+    // insert separators if necessary
+    if (vsep != 0 && vsep != pVCodecs->count())
+        pVCodecs->insertSeparator(vsep);
+    if (asep != 0 && asep != pACodecs->count())
+        pACodecs->insertSeparator(asep);
+}
+
+QString LibavIteraction::getExtension(const QString & format)
+{
+    return formats[format].extension;
+}
+
+QString tr(QString a)
+{
+    return a;
+}
+
+// get information abaout file (duration, resolution etc) in multiline string
+QString LibavIteraction::getFileInfo(const QString & filepath)
+{
+    AVFormatContext* pContext = NULL;
+    QByteArray utf8path = filepath.toUtf8();
+    if (avformat_open_input(&pContext, utf8path.data(), NULL, NULL) < 0)
+        return "";
+#if LIBAFORMAT_VERSION_MAJOR < 54
+    if (av_find_stream_info(pContext) < 0)
+#else
+    if (avformat_find_stream_info(pContext, NULL) < 0)
+#endif
+        return "";
+
+    int s = float(pContext->duration)/AV_TIME_BASE;
+    QString desc = QString(tr("Duration: %1m %2s\n")).arg(s/60).arg(s%60);
+    for (int i = 0; i < (int)pContext->nb_streams; i++)
+    {
+        AVStream* pStream = pContext->streams[i];
+        if (!pStream)
+            continue;
+        AVCodecContext* pCodec = pContext->streams[i]->codec;
+        if (!pCodec)
+            continue;
+
+        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);
+            }
+        }
+        else if (pCodec->codec_type == AVMEDIA_TYPE_AUDIO)
+            desc += tr("Audio: ");
+        else
+            continue;
+        AVCodec* pDecoder = avcodec_find_decoder(pCodec->codec_id);
+        desc += pDecoder? pDecoder->name : "unknown";
+        desc += "\n";
+    }
+    AVDictionaryEntry* pComment = av_dict_get(pContext->metadata, "comment", NULL, 0);
+    if (pComment)
+        desc += QString("\n") + pComment->value;
+#if LIBAFORMAT_VERSION_MAJOR < 54
+    av_close_input_file(pContext);
+#else
+    avformat_close_input(&pContext);
+#endif
+    return desc;
+}
+
+#else
+LibavIteraction::LibavIteraction()
+{
+
+}
+
+void LibavIteraction::fillFormats(QComboBox * pFormats)
+{
+    Q_UNUSED(pFormats);
+}
+
+void LibavIteraction::fillCodecs(const QString & format, QComboBox * pVCodecs, QComboBox * pACodecs)
+{
+    Q_UNUSED(format);
+    Q_UNUSED(pVCodecs);
+    Q_UNUSED(pACodecs);
+}
+
+QString LibavIteraction::getExtension(const QString & format)
+{
+    Q_UNUSED(format);
+
+    return QString();
+}
+
+QString LibavIteraction::getFileInfo(const QString & filepath)
+{
+    Q_UNUSED(filepath);
+
+    return QString();
+}
+#endif
+
+LibavIteraction & LibavIteraction::instance()
+{
+    static LibavIteraction instance;
+    return instance;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/util/libav_iteraction.h	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,49 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBAV_ITERACTION
+#define LIBAV_ITERACTION
+
+#include <QComboBox>
+
+/**
+ * @brief Class for interacting with ffmpeg/libav libraries
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Singleton_pattern">singleton pattern</a>
+ */
+class LibavIteraction
+{
+    LibavIteraction();
+
+public:
+
+    static LibavIteraction & instance();
+
+    // fill combo box with known file formats
+    void fillFormats(QComboBox * pFormats);
+
+    // fill combo boxes with known codecs for given formats
+    void fillCodecs(const QString & format, QComboBox * pVCodecs, QComboBox * pACodecs);
+
+    QString getExtension(const QString & format);
+
+    // get information about file (duration, resolution etc) in multiline string
+    QString getFileInfo(const QString & filepath);
+};
+
+#endif // LIBAV_ITERACTION
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake_modules/FindFFMPEG.cmake	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,79 @@
+# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
+# Once done this will define
+#
+#  FFMPEG_FOUND - system has ffmpeg or libav
+#  FFMPEG_INCLUDE_DIR - the ffmpeg include directory
+#  FFMPEG_LIBRARIES - Link these to use ffmpeg
+#  FFMPEG_LIBAVCODEC
+#  FFMPEG_LIBAVFORMAT
+#  FFMPEG_LIBAVUTIL
+#
+#  Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
+#  Modified for other libraries by Lasse Kärkkäinen <tronic>
+#  Modified for Hedgewars by Stepik777
+#
+#  Redistribution and use is allowed according to the terms of the New
+#  BSD license.
+#
+
+if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+  # in cache already
+  set(FFMPEG_FOUND TRUE)
+else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+  # use pkg-config to get the directories and then use these values
+  # in the FIND_PATH() and FIND_LIBRARY() calls
+  find_package(PkgConfig)
+  if (PKG_CONFIG_FOUND)
+    pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
+    pkg_check_modules(_FFMPEG_AVFORMAT libavformat)
+    pkg_check_modules(_FFMPEG_AVUTIL libavutil)
+  endif (PKG_CONFIG_FOUND)
+
+  find_path(FFMPEG_AVCODEC_INCLUDE_DIR
+    NAMES libavcodec/avcodec.h
+    PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS} /usr/include /usr/local/include /opt/local/include /sw/include
+    PATH_SUFFIXES ffmpeg libav
+  )
+
+  find_library(FFMPEG_LIBAVCODEC
+    NAMES avcodec
+    PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
+  )
+
+  find_library(FFMPEG_LIBAVFORMAT
+    NAMES avformat
+    PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
+  )
+
+  find_library(FFMPEG_LIBAVUTIL
+    NAMES avutil
+    PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
+  )
+
+  if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT)
+    set(FFMPEG_FOUND TRUE)
+  endif()
+
+  if (FFMPEG_FOUND)
+    set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
+
+    set(FFMPEG_LIBRARIES
+      ${FFMPEG_LIBAVCODEC}
+      ${FFMPEG_LIBAVFORMAT}
+      ${FFMPEG_LIBAVUTIL}
+    )
+
+  endif (FFMPEG_FOUND)
+
+  if (FFMPEG_FOUND)
+    if (NOT FFMPEG_FIND_QUIETLY)
+      message(STATUS "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
+    endif (NOT FFMPEG_FIND_QUIETLY)
+  else (FFMPEG_FOUND)
+    if (FFMPEG_FIND_REQUIRED)
+      message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil")
+    endif (FFMPEG_FIND_REQUIRED)
+  endif (FFMPEG_FOUND)
+
+endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+
--- a/gameServer/Actions.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/Actions.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -146,9 +146,14 @@
     io $
         infoM "Clients" (show ci ++ " quits: " ++ B.unpack msg)
 
-    processAction $ AnswerClients [chan] ["BYE", msg]
     when loggedIn $ processAction $ AnswerClients clientsChans ["LOBBY:LEFT", clNick, msg]
 
+    mapM processAction
+        [
+        AnswerClients [chan] ["BYE", msg]
+        , ModifyClient (\c -> c{logonPassed = False}) -- this will effectively hide client from others while he isn't deleted from list
+        ]
+
     s <- get
     put $! s{removedClients = ci `Set.insert` removedClients s}
 
@@ -216,7 +221,7 @@
     chans <- othersChans
 
     if master then
-        if gameProgress && playersNum > 1 then
+        if playersNum > 1 then
             mapM_ processAction [ChangeMaster, NoticeMessage AdminLeft, RemoveClientTeams ci, AnswerClients chans ["LEFT", clNick, msg]]
             else
             processAction RemoveRoom
@@ -225,7 +230,7 @@
 
     -- when not removing room
     ready <- client's isReady
-    when (not master || (gameProgress && playersNum > 1)) . io $ do
+    when (not master || playersNum > 1) . io $ do
         modifyRoom rnc (\r -> r{
                 playersIn = playersIn r - 1,
                 readyPlayers = if ready then readyPlayers r - 1 else readyPlayers r
@@ -418,11 +423,22 @@
 processAction JoinLobby = do
     chan <- client's sendChan
     clientNick <- client's nick
-    (lobbyNicks, clientsChans) <- liftM (unzip . Prelude.map (nick &&& sendChan) . Prelude.filter logonPassed) $! allClientsS
-    mapM_ processAction $
-        AnswerClients clientsChans ["LOBBY:JOINED", clientNick]
-        : AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)
-        : [ModifyClient (\cl -> cl{logonPassed = True}), SendServerMessage]
+    isAuthenticated <- liftM (not . B.null) $ client's webPassword
+    isAdmin <- client's isAdministrator
+    loggedInClients <- liftM (Prelude.filter logonPassed) $! allClientsS
+    let (lobbyNicks, clientsChans) = unzip . L.map (nick &&& sendChan) $ loggedInClients
+    let authenticatedNicks = L.map nick . L.filter (not . B.null . webPassword) $ loggedInClients
+    let adminsNicks = L.map nick . L.filter isAdministrator $ loggedInClients
+    let clFlags = B.concat . L.concat $ [["u" | isAuthenticated], ["a" | isAdmin]]
+    mapM_ processAction . concat $ [
+        [AnswerClients clientsChans ["LOBBY:JOINED", clientNick]]
+        , [AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)]
+        , [AnswerClients [chan] ("CLIENT_FLAGS" : "+u" : authenticatedNicks) | not $ null authenticatedNicks]
+        , [AnswerClients [chan] ("CLIENT_FLAGS" : "+a" : adminsNicks) | not $ null adminsNicks]
+        , [AnswerClients (chan : clientsChans) ["CLIENT_FLAGS",  B.concat["+" , clFlags], clientNick] | not $ B.null clFlags]
+        , [ModifyClient (\cl -> cl{logonPassed = True})]
+        , [SendServerMessage]
+        ]
 
 
 processAction (KickClient kickId) = do
@@ -453,9 +469,9 @@
 
 processAction BanList = do
     ch <- client's sendChan
-    bans <- gets (bans . serverInfo)
+    bans <- gets (B.pack . unlines . map show . bans . serverInfo)
     processAction $
-        AnswerClients [ch] ["BANLIST", B.pack $ show bans]
+        AnswerClients [ch] ["BANLIST", bans]
 
 
 
@@ -522,9 +538,11 @@
     where
         kickTimeouted rnc ci = do
             pq <- io $ client'sM rnc pingsQueue ci
-            when (pq > 0) $
+            when (pq > 0) $ do
                 withStateT (\as -> as{clientIndex = Just ci}) $
                     processAction (ByeClient "Ping timeout")
+--                when (pq > 1) $
+--                    processAction $ DeleteClient ci -- smth went wrong with client io threads, issue DeleteClient here
 
 
 processAction StatsAction = do
--- a/gameServer/CoreTypes.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/CoreTypes.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -94,6 +94,7 @@
         readyPlayers :: !Int,
         isRestrictedJoins :: Bool,
         isRestrictedTeams :: Bool,
+        roomBansList :: [B.ByteString],
         mapParams :: Map.Map B.ByteString B.ByteString,
         params :: Map.Map B.ByteString [B.ByteString]
     }
@@ -111,6 +112,7 @@
         0
         False
         False
+        []
         (
             Map.fromList $ Prelude.zipWith (,)
                 ["MAP", "MAPGEN", "MAZE_SIZE", "SEED", "TEMPLATE"]
--- a/gameServer/HWProtoInRoomState.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/HWProtoInRoomState.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -278,6 +278,14 @@
     where
         engineMsg cl = toEngineMsg $ B.concat ["b", nick cl, "(team): ", msg, "\x20\x20"]
 
+handleCmd_inRoom ["BAN", banNick] = do
+    (_, rnc) <- ask
+    maybeClientId <- clientByNick banNick
+    let banId = fromJust maybeClientId
+    master <- liftM isMaster thisClient
+    return [ModifyRoom (\r -> r{roomBansList = (host $ rnc `client` banId) : roomBansList r}) | master && isJust maybeClientId]
+
+
 handleCmd_inRoom ["LIST"] = return [] -- for old clients (<= 0.9.17)
 
 handleCmd_inRoom (s:_) = return [ProtocolError $ "Incorrect command '" `B.append` s `B.append` "' (state: in room)"]
--- a/gameServer/HWProtoLobbyState.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/HWProtoLobbyState.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -70,11 +70,14 @@
     let jRoomClients = map (client irnc) $ roomClients irnc jRI
     let nicks = map nick jRoomClients
     let chans = map sendChan (cl : jRoomClients)
+    let isBanned = host cl `elem` roomBansList jRoom
     return $
         if isNothing maybeRI || not sameProto then
             [Warning "No such room"]
             else if isRestrictedJoins jRoom then
             [Warning "Joining restricted"]
+            else if isBanned then
+            [Warning "You are banned in this room"]
             else if roomPassword /= password jRoom then
             [NoticeMessage WrongPassword]
             else
@@ -183,7 +186,7 @@
 
 handleCmd_lobby ["RESTART_SERVER"] = do
     cl <- thisClient
-    return [RestartServer]
+    return [RestartServer | isAdministrator cl]
 
 
 handleCmd_lobby _ = return [ProtocolError "Incorrect command (state: in lobby)"]
--- a/gameServer/ServerCore.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/ServerCore.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -43,7 +43,7 @@
 
         ClientMessage (ci, cmd) -> do
             liftIO $ debugM "Clients" $ show ci ++ ": " ++ show cmd
-            
+
             removed <- gets removedClients
             unless (ci `Set.member` removed) $ do
                 modify (\s -> s{clientIndex = Just ci})
@@ -75,8 +75,6 @@
             (fromJust $ serverSocket si)
             (coreChan si)
 
-    return ()
-
     _ <- forkIO $ timerLoop 0 $ coreChan si
 
     startDBConnection si
--- a/hedgewars/ArgParsers.inc	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/ArgParsers.inc	Thu Aug 30 13:02:19 2012 -0400
@@ -63,6 +63,21 @@
     cLocaleFName:= ParamStr(17);
 end;
 
+{$IFDEF USE_VIDEO_RECORDING}
+procedure internalStartVideoRecordingWithParameters();
+begin
+    internalStartGameWithParameters();
+    GameType:= gmtRecord;
+    cVideoFramerateNum:= StrToInt(ParamStr(18));
+    cVideoFramerateDen:= StrToInt(ParamStr(19));
+    RecPrefix:= ParamStr(20);
+    cAVFormat:= ParamStr(21);
+    cVideoCodec:= ParamStr(22);
+    cVideoQuality:= StrToInt(ParamStr(23));
+    cAudioCodec:= ParamStr(24);
+end;
+{$ENDIF}
+
 procedure setVideo(screenWidth: LongInt; screenHeight: LongInt; bitsStr: LongInt);
 begin
     cScreenWidth:= screenWidth;
--- a/hedgewars/CMakeLists.txt	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/CMakeLists.txt	Thu Aug 30 13:02:19 2012 -0400
@@ -3,6 +3,7 @@
 find_package(SDL_net)
 find_package(SDL_ttf)
 find_package(SDL_mixer)
+find_package(FFMPEG)
 
 include(${CMAKE_MODULE_PATH}/FindSDL_Extras.cmake)
 
@@ -33,7 +34,9 @@
     uGame.pas
     uGears.pas
     uGearsHandlers.pas
+    uGearsHandlersRope.pas
     uGearsRender.pas
+    uGearsUtils.pas
     uIO.pas
     uInputHandler.pas
     uLand.pas
@@ -60,6 +63,7 @@
     uTypes.pas
     uUtils.pas
     uVariables.pas
+    uVideoRec.pas
     uVisualGears.pas
     uWorld.pas
     GSHandlers.inc
@@ -183,9 +187,31 @@
     message(STATUS "PNG screenshots disabled per user request, using BMP format")
 endif()
 
+if(NOT NO_VIDEOREC)
+    if(${FFMPEG_FOUND})
+        message(STATUS "Compiling with video recording")
+        include_directories(${FFMPEG_INCLUDE_DIR})
+        set(pascal_flags "-dUSE_VIDEO_RECORDING" ${pascal_flags})
+        set(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH})
+        IF (WIN32)
+            # there are some problems with linking our avwrapper as static lib, so link it as shared
+            add_library(avwrapper SHARED avwrapper.c)
+            target_link_libraries(avwrapper ${FFMPEG_LIBRARIES})
+            install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}avwrapper${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION ${target_dir})
+        ELSE()
+            add_library(avwrapper STATIC avwrapper.c)
+            set(pascal_flags "-k${FFMPEG_LIBAVCODEC}" "-k${FFMPEG_LIBAVFORMAT}" "-k${FFMPEG_LIBAVUTIL}" ${pascal_flags})
+         #   set(pascal_flags "-k${LIBRARY_OUTPUT_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}avwrapper${CMAKE_STATIC_LIBRARY_SUFFIX}" ${pascal_flags})
+        ENDIF()
+    else()
+        message(STATUS "FFMPEG library not found, video recording will be disabled")
+    endif()
+else()
+    message(STATUS "Video recording disabled by user")
+endif()
+
 set(fpc_flags ${noexecstack_flags} ${pascal_flags} ${hwengine_project})
 
-
 IF(NOT APPLE)
     #here is the command for standard executables or for shared library
     add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/${engine_output_name}${CMAKE_EXECUTABLE_SUFFIX}"
@@ -225,10 +251,9 @@
 #this command is a workaround to some inlining issues present in older
 # FreePascal versions and fixed in 2.6, That version is mandatory on OSX,
 # hence the command is not needed there
-if(NOT APPLE)
-    add_custom_target(ENGINECLEAN COMMAND ${CMAKE_BUILD_TOOL} "clean" "${PROJECT_BINARY_DIR}" "${hedgewars_SOURCE_DIR}/hedgewars")
-    add_dependencies(${engine_output_name} ENGINECLEAN)
-endif()
+# if(NOT APPLE)
+#    add_custom_target(ENGINECLEAN COMMAND ${CMAKE_BUILD_TOOL} "clean" "${PROJECT_BINARY_DIR}" "${hedgewars_SOURCE_DIR}/hedgewars")
+#    add_dependencies(${engine_output_name} ENGINECLEAN)
+# endif()
 
 install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/${engine_output_name}${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION ${target_dir})
-
--- a/hedgewars/GSHandlers.inc	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/GSHandlers.inc	Thu Aug 30 13:02:19 2012 -0400
@@ -140,28 +140,6 @@
     ScriptCall('onHogRestore', HH^.Gear^.Uid)
 end;
 
-////////////////////////////////////////////////////////////////////////////////
-procedure CheckCollision(Gear: PGear); inline;
-begin
-    if TestCollisionXwithGear(Gear, hwSign(Gear^.dX))
-    or (TestCollisionYwithGear(Gear, hwSign(Gear^.dY)) <> 0) then
-        Gear^.State := Gear^.State or gstCollision
-    else
-        Gear^.State := Gear^.State and (not gstCollision)
-end;
-
-procedure CheckCollisionWithLand(Gear: PGear); inline;
-begin
-    if TestCollisionX(Gear, hwSign(Gear^.dX))
-    or TestCollisionY(Gear, hwSign(Gear^.dY)) then
-        Gear^.State := Gear^.State or gstCollision
-    else 
-        Gear^.State := Gear^.State and (not gstCollision)
-end;
-
-
-////////////////////////////////////////////////////////////////////////////////
-
 
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepDrowningGear(Gear: PGear);
@@ -191,11 +169,24 @@
     collV, collH: LongInt;
     land: word;
 begin
-    // clip velocity at 1.9 - over 1 per pixel, but really shouldn't cause many actual problems.
-    if Gear^.dX.QWordValue > 8160437862 then
-        Gear^.dX.QWordValue:= 8160437862;
-    if Gear^.dY.QWordValue > 8160437862 then
-        Gear^.dY.QWordValue:= 8160437862;
+    // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems.
+{$IFNDEF WEB}
+    if Gear^.dX.Round > 2 then
+        Gear^.dX.QWordValue:= 8589934592;
+    if Gear^.dY.Round > 2 then
+        Gear^.dY.QWordValue:= 8589934592;
+{$ELSE}
+    if Gear^.dX.Round > 2 then
+        begin
+        Gear^.dX.Round:= 2;
+        Gear^.dX.Frac:= 0
+        end;
+    if Gear^.dY.QWordValue > 2 then
+        begin
+        Gear^.dY.Round:= 2;
+        Gear^.dY.Frac:= 0
+        end;
+{$ENDIF}
     Gear^.State := Gear^.State and (not gstCollision);
     collV := 0;
     collH := 0;
@@ -541,7 +532,7 @@
         kick:= hwRound((hwAbs(Gear^.dX)+hwAbs(Gear^.dY)) * _20);
         Gear^.dY.isNegative:= not Gear^.dY.isNegative;
         Gear^.dX.isNegative:= not Gear^.dX.isNegative;
-        AmmoShove(Gear, 1, kick);
+        AmmoShove(Gear, 0, kick);
         for i:= 15 + kick div 10 downto 0 do
             begin
             particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust);
@@ -610,11 +601,10 @@
     // move back to cloud layer
         if yy > cWaterLine then
             move:= true
-        else if ((yy and LAND_HEIGHT_MASK) <> 0)
-        or (xx > LAND_WIDTH + 512) or (xx < -512) then
+        else if (xx > snowRight) or (xx < snowLeft) then
             move:=true
         // Solid pixel encountered
-        else if ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
+        else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
             begin
             lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible);
             // If there's room below keep falling
@@ -724,8 +714,8 @@
         exit
         end;
     Gear^.Pos:= 0;
-    Gear^.X:= int2hwFloat(GetRandom(LAND_WIDTH+1024)-512);
-    Gear^.Y:= int2hwFloat(750+(GetRandom(50)-25));
+    Gear^.X:= int2hwFloat(GetRandom(snowRight-snowLeft)+snowLeft);
+    Gear^.Y:= int2hwFloat(LAND_HEIGHT-1300+(GetRandom(50)-25));
     Gear^.State:= Gear^.State or gstInvisible;
     end
 end;
@@ -924,7 +914,13 @@
             Gear^.State := Gear^.State or gstAnimation
             end;
             exit
-        end
+        end else
+        if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then
+            begin
+            DeleteGear(Gear);
+            AfterAttack;
+            exit
+            end
     else
         inc(Gear^.Timer);
 
@@ -1401,436 +1397,6 @@
 end;
 
 ////////////////////////////////////////////////////////////////////////////////
-
-procedure doStepRope(Gear: PGear);
-forward;
-
-procedure doStepRopeAfterAttack(Gear: PGear);
-var 
-    HHGear: PGear;
-begin
-    HHGear := Gear^.Hedgehog^.Gear;
-    if ((HHGear^.State and gstHHDriven) = 0)
-    or (CheckGearDrowning(HHGear))
-    or (TestCollisionYwithGear(HHGear, 1) <> 0) then
-        begin
-        DeleteGear(Gear);
-        isCursorVisible := false;
-        ApplyAmmoChanges(HHGear^.Hedgehog^);
-        exit
-        end;
-
-    HedgehogChAngle(HHGear);
-
-    if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
-        SetLittle(HHGear^.dX);
-
-    if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
-        HHGear^.dY := _0;
-    HHGear^.X := HHGear^.X + HHGear^.dX;
-    HHGear^.Y := HHGear^.Y + HHGear^.dY;
-    HHGear^.dY := HHGear^.dY + cGravity;
-    
-    if (GameFlags and gfMoreWind) <> 0 then
-        HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;
-
-    if (Gear^.Message and gmAttack) <> 0 then
-        begin
-        Gear^.X := HHGear^.X;
-        Gear^.Y := HHGear^.Y;
-
-        ApplyAngleBounds(Gear^.Hedgehog^, amRope);
-
-        Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX);
-        Gear^.dY := -AngleCos(HHGear^.Angle);
-        Gear^.Friction := _4_5 * cRopePercent;
-        Gear^.Elasticity := _0;
-        Gear^.State := Gear^.State and (not gsttmpflag);
-        Gear^.doStep := @doStepRope;
-        end
-end;
-
-procedure RopeDeleteMe(Gear, HHGear: PGear);
-begin
-    with HHGear^ do
-        begin
-        Message := Message and (not gmAttack);
-        State := (State or gstMoving) and (not gstWinner);
-        end;
-    DeleteGear(Gear)
-end;
-
-procedure RopeWaitCollision(Gear, HHGear: PGear);
-begin
-    with HHGear^ do
-        begin
-        Message := Message and (not gmAttack);
-        State := State or gstMoving;
-        end;
-    RopePoints.Count := 0;
-    Gear^.Elasticity := _0;
-    Gear^.doStep := @doStepRopeAfterAttack
-end;
-
-procedure doStepRopeWork(Gear: PGear);
-var 
-    HHGear: PGear;
-    len, tx, ty, nx, ny, ropeDx, ropeDy, mdX, mdY: hwFloat;
-    lx, ly, cd: LongInt;
-    haveCollision,
-    haveDivided: boolean;
-
-begin
-    HHGear := Gear^.Hedgehog^.Gear;
-
-    if ((HHGear^.State and gstHHDriven) = 0)
-       or (CheckGearDrowning(HHGear)) or (Gear^.PortalCounter <> 0) then
-        begin
-        PlaySound(sndRopeRelease);
-        RopeDeleteMe(Gear, HHGear);
-        exit
-        end;
-
-    if (Gear^.Message and gmLeft  <> 0) and (not TestCollisionXwithGear(HHGear, -1)) then
-        HHGear^.dX := HHGear^.dX - _0_0002;
-
-    if (Gear^.Message and gmRight <> 0) and (not TestCollisionXwithGear(HHGear,  1)) then
-        HHGear^.dX := HHGear^.dX + _0_0002;
-
-    // vector between hedgehog and rope attaching point
-    ropeDx := HHGear^.X - Gear^.X;
-    ropeDy := HHGear^.Y - Gear^.Y;
-
-    if TestCollisionYwithGear(HHGear, 1) = 0 then
-        begin
-
-        // depending on the rope vector we know which X-side to check for collision
-        // in order to find out if the hog can still be moved by gravity
-        if ropeDx.isNegative = RopeDy.IsNegative then
-            cd:= -1
-        else
-            cd:= 1;
-
-        // apply gravity if there is no obstacle
-        if not TestCollisionXwithGear(HHGear, cd) then
-            HHGear^.dY := HHGear^.dY + cGravity;
-
-        if (GameFlags and gfMoreWind) <> 0 then
-            // apply wind if there's no obstacle
-            if not TestCollisionXwithGear(HHGear, hwSign(cWindSpeed)) then
-                HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;
-        end;
-
-    mdX := ropeDx + HHGear^.dX;
-    mdY := ropeDy + HHGear^.dY;
-    len := _1 / Distance(mdX, mdY);
-    // rope vector plus hedgehog direction vector normalized
-    mdX := mdX * len;
-    mdY := mdY * len;
-
-    // for visual purposes only
-    Gear^.dX := mdX;
-    Gear^.dY := mdY;
-
-    /////
-    tx := HHGear^.X;
-    ty := HHGear^.Y;
-
-    if ((Gear^.Message and gmDown) <> 0) and (Gear^.Elasticity < Gear^.Friction) then
-        if not (TestCollisionXwithGear(HHGear, hwSign(ropeDx))
-        or (TestCollisionYwithGear(HHGear, hwSign(ropeDy)) <> 0)) then
-            Gear^.Elasticity := Gear^.Elasticity + _0_3;
-
-    if ((Gear^.Message and gmUp) <> 0) and (Gear^.Elasticity > _30) then
-        if not (TestCollisionXwithGear(HHGear, -hwSign(ropeDx))
-        or (TestCollisionYwithGear(HHGear, -hwSign(ropeDy)) <> 0)) then
-            Gear^.Elasticity := Gear^.Elasticity - _0_3;
-
-    HHGear^.X := Gear^.X + mdX * Gear^.Elasticity;
-    HHGear^.Y := Gear^.Y + mdY * Gear^.Elasticity;
-
-    HHGear^.dX := HHGear^.X - tx;
-    HHGear^.dY := HHGear^.Y - ty;
-    ////
-
-
-    haveDivided := false;
-    // check whether rope needs dividing
-
-    len := Gear^.Elasticity - _5;
-    nx := Gear^.X + mdX * len;
-    ny := Gear^.Y + mdY * len;
-    tx := mdX * _0_3; // should be the same as increase step
-    ty := mdY * _0_3;
-
-    while len > _3 do
-        begin
-        lx := hwRound(nx);
-        ly := hwRound(ny);
-        if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and ((Land[ly, lx] and $FF00) <> 0) then
-            begin
-            ny := _1 / Distance(ropeDx, ropeDy);
-            // old rope pos
-            nx := ropeDx * ny;
-            ny := ropeDy * ny;
-
-            with RopePoints.ar[RopePoints.Count] do
-                begin
-                X := Gear^.X;
-                Y := Gear^.Y;
-                if RopePoints.Count = 0 then
-                    RopePoints.HookAngle := DxDy2Angle(Gear^.dY, Gear^.dX);
-                b := (nx * HHGear^.dY) > (ny * HHGear^.dX);
-                dLen := len
-                end;
-                
-            with RopePoints.rounded[RopePoints.Count] do
-                begin
-                X := hwRound(Gear^.X);
-                Y := hwRound(Gear^.Y);
-                end;
-
-            Gear^.X := Gear^.X + nx * len;
-            Gear^.Y := Gear^.Y + ny * len;
-            inc(RopePoints.Count);
-            TryDo(RopePoints.Count <= MAXROPEPOINTS, 'Rope points overflow', true);
-            Gear^.Elasticity := Gear^.Elasticity - len;
-            Gear^.Friction := Gear^.Friction - len;
-            haveDivided := true;
-            break
-            end;
-        nx := nx - tx;
-        ny := ny - ty;
-
-        // len := len - _0_3 // should be the same as increase step
-        len.QWordValue := len.QWordValue - _0_3.QWordValue;
-        end;
-
-    if not haveDivided then
-        if RopePoints.Count > 0 then // check whether the last dividing point could be removed
-            begin
-            tx := RopePoints.ar[Pred(RopePoints.Count)].X;
-            ty := RopePoints.ar[Pred(RopePoints.Count)].Y;
-            mdX := tx - Gear^.X;
-            mdY := ty - Gear^.Y;
-            if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * (ty - HHGear^.Y) > (tx - HHGear^.X) * mdY) then
-                begin
-                dec(RopePoints.Count);
-                Gear^.X := RopePoints.ar[RopePoints.Count].X;
-                Gear^.Y := RopePoints.ar[RopePoints.Count].Y;
-                Gear^.Elasticity := Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen;
-                Gear^.Friction := Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen;
-
-                // restore hog position
-                len := _1 / Distance(mdX, mdY);
-                mdX := mdX * len;
-                mdY := mdY * len;
-
-                HHGear^.X := Gear^.X - mdX * Gear^.Elasticity;
-                HHGear^.Y := Gear^.Y - mdY * Gear^.Elasticity;
-                end
-            end;
-
-    haveCollision := false;
-    if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
-        begin
-        HHGear^.dX := -_0_6 * HHGear^.dX;
-        haveCollision := true
-        end;
-    if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0 then
-        begin
-        HHGear^.dY := -_0_6 * HHGear^.dY;
-        haveCollision := true
-        end;
-
-    if haveCollision and (Gear^.Message and (gmLeft or gmRight) <> 0) and (Gear^.Message and (gmUp or gmDown) <> 0) then
-        begin
-        HHGear^.dX := SignAs(hwAbs(HHGear^.dX) + _0_2, HHGear^.dX);
-        HHGear^.dY := SignAs(hwAbs(HHGear^.dY) + _0_2, HHGear^.dY)
-        end;
-
-    len := hwSqr(HHGear^.dX) + hwSqr(HHGear^.dY);
-    if len > _0_64 then
-        begin
-        len := _0_8 / hwSqrt(len);
-        HHGear^.dX := HHGear^.dX * len;
-        HHGear^.dY := HHGear^.dY * len;
-        end;
-
-    haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)]) <> 0);
-
-    if not haveCollision then
-        begin
-        // backup gear location
-        tx:= Gear^.X;
-        ty:= Gear^.Y;
-
-        if RopePoints.Count > 0 then
-            begin
-            // set gear location to the remote end of the rope, the attachment point
-            Gear^.X:= RopePoints.ar[0].X;
-            Gear^.Y:= RopePoints.ar[0].Y;
-            end;
-
-        CheckCollision(Gear);
-        // if we haven't found any collision yet then check the other side too
-        if (Gear^.State and gstCollision) = 0 then
-            begin
-            Gear^.dX.isNegative:= not Gear^.dX.isNegative;
-            Gear^.dY.isNegative:= not Gear^.dY.isNegative;
-            CheckCollision(Gear);
-            Gear^.dX.isNegative:= not Gear^.dX.isNegative;
-            Gear^.dY.isNegative:= not Gear^.dY.isNegative;
-            end;
-
-        haveCollision:= (Gear^.State and gstCollision) <> 0;
-
-        // restore gear location
-        Gear^.X:= tx;
-        Gear^.Y:= ty;
-        end;
-
-    // if the attack key is pressed, lose rope contact as well
-    if (Gear^.Message and gmAttack) <> 0 then
-        haveCollision:= false;
-
-    if not haveCollision then
-        begin
-        if (Gear^.State and gsttmpFlag) <> 0 then
-            begin
-            PlaySound(sndRopeRelease);
-            if Gear^.Hedgehog^.CurAmmoType <> amParachute then
-                RopeWaitCollision(Gear, HHGear)
-            else
-                RopeDeleteMe(Gear, HHGear)
-            end
-        end
-    else
-        if (Gear^.State and gsttmpFlag) = 0 then
-            Gear^.State := Gear^.State or gsttmpFlag;
-end;
-
-procedure RopeRemoveFromAmmo(Gear, HHGear: PGear);
-begin
-    if (Gear^.State and gstAttacked) = 0 then
-        begin
-        OnUsedAmmo(HHGear^.Hedgehog^);
-        Gear^.State := Gear^.State or gstAttacked
-        end;
-    ApplyAmmoChanges(HHGear^.Hedgehog^)
-end;
-
-procedure doStepRopeAttach(Gear: PGear);
-var 
-    HHGear: PGear;
-    tx, ty, tt: hwFloat;
-begin
-    Gear^.X := Gear^.X - Gear^.dX;
-    Gear^.Y := Gear^.Y - Gear^.dY;
-    Gear^.Elasticity := Gear^.Elasticity + _1;
-
-    HHGear := Gear^.Hedgehog^.Gear;
-    DeleteCI(HHGear);
-
-    if (HHGear^.State and gstMoving) <> 0 then
-        begin
-        if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
-            SetLittle(HHGear^.dX);
-        if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
-            HHGear^.dY := _0;
-
-        HHGear^.X := HHGear^.X + HHGear^.dX;
-        Gear^.X := Gear^.X + HHGear^.dX;
-
-        if TestCollisionYwithGear(HHGear, 1) <> 0 then
-            begin
-            CheckHHDamage(HHGear);
-            HHGear^.dY := _0
-            //HHGear^.State:= HHGear^.State and (not (gstHHJumping or gstHHHJump));
-            end
-        else
-            begin
-            HHGear^.Y := HHGear^.Y + HHGear^.dY;
-            Gear^.Y := Gear^.Y + HHGear^.dY;
-            HHGear^.dY := HHGear^.dY + cGravity;
-            if (GameFlags and gfMoreWind) <> 0 then
-                HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density
-            end;
-
-        tt := Gear^.Elasticity;
-        tx := _0;
-        ty := _0;
-        while tt > _20 do
-            begin
-            if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] and $FF00) <> 0) then
-                begin
-                Gear^.X := Gear^.X + tx;
-                Gear^.Y := Gear^.Y + ty;
-                Gear^.Elasticity := tt;
-                Gear^.doStep := @doStepRopeWork;
-                PlaySound(sndRopeAttach);
-                with HHGear^ do
-                    begin
-                    State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
-                    Message := Message and (not gmAttack)
-                    end;
-
-                RopeRemoveFromAmmo(Gear, HHGear);
-
-                tt := _0;
-                exit
-                end;
-            tx := tx + Gear^.dX + Gear^.dX;
-            ty := ty + Gear^.dY + Gear^.dY;
-            tt := tt - _2;
-            end;
-        end;
-
-    CheckCollision(Gear);
-
-    if (Gear^.State and gstCollision) <> 0 then
-        if Gear^.Elasticity < _10 then
-            Gear^.Elasticity := _10000
-    else
-        begin
-        Gear^.doStep := @doStepRopeWork;
-        PlaySound(sndRopeAttach);
-        with HHGear^ do
-            begin
-            State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
-            Message := Message and (not gmAttack)
-            end;
-
-        RopeRemoveFromAmmo(Gear, HHGear);
-
-        exit
-        end;
-
-    if (Gear^.Elasticity > Gear^.Friction)
-        or ((Gear^.Message and gmAttack) = 0)
-        or ((HHGear^.State and gstHHDriven) = 0)
-        or (HHGear^.Damage > 0) then
-            begin
-            with Gear^.Hedgehog^.Gear^ do
-                begin
-                State := State and (not gstAttacking);
-                Message := Message and (not gmAttack)
-                end;
-        DeleteGear(Gear);
-	exit;
-        end;
-    if CheckGearDrowning(HHGear) then DeleteGear(Gear)
-end;
-
-procedure doStepRope(Gear: PGear);
-begin
-    Gear^.dX := - Gear^.dX;
-    Gear^.dY := - Gear^.dY;
-    Gear^.doStep := @doStepRopeAttach;
-    PlaySound(sndRopeShot)
-end;
-
-////////////////////////////////////////////////////////////////////////////////
 procedure doStepMine(Gear: PGear);
 var vg: PVisualGear;
 begin
@@ -1930,7 +1496,7 @@
     or TestCollisionXwithGear(Gear, -2) 
     or (TestCollisionYwithGear(Gear, 2) <> 0) then
         begin
-        if (hwAbs(Gear^.dX) > _0) or (hwAbs(Gear^.dY) > _0) then
+        if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then
             begin
             PlaySound(sndRopeAttach);
             Gear^.dX:= _0;
@@ -2406,7 +1972,7 @@
                 //DrawExplosion(gX, gY, 4);
                 
                 if ((GameTicks and $7) = 0) and (Random(2) = 0) then
-                    for i:= 1 to Random(2)+1 do
+                    for i:= Random(2) downto 0 do
                         AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
                         
                 if Gear^.Health > 0 then
@@ -2420,7 +1986,7 @@
                     begin
                     DrawExplosion(gX, gY, 4);
 
-                    for i:= 0 to Random(3) do
+                    for i:= Random(3) downto 0 do
                         AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
                     end;
 
@@ -2438,20 +2004,12 @@
         if not sticky then
             begin
             if ((GameTicks and $3) = 0) and (Random(1) = 0) then
-                begin
-                for i:= 1 to Random(2)+1 do
-                    begin
+                for i:= Random(2) downto 0 do
                     AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
-                    end;
-                end;
             end
         else
-            begin
-            for i:= 0 to Random(3) do
-                begin
+            for i:= Random(3) downto 0 do
                 AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
-                end;
-            end;
 
         DeleteGear(Gear)
         end;
@@ -3007,7 +2565,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 const cakeh =   27;
-    cakeDmg =   75;
 var 
     CakePoints: array[0..Pred(cakeh)] of record
         x, y: hwFloat;
@@ -3091,6 +2648,19 @@
     if Gear^.Tag < 7 then
         exit;
 
+    dec(Gear^.Health);
+    Gear^.Timer := Gear^.Health*10;
+    if Gear^.Health mod 100 = 0 then
+        Gear^.PortalCounter:= 0;
+    // This is not seconds, but at least it is *some* feedback
+    if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then
+        begin
+        FollowGear := Gear;
+        Gear^.RenderTimer := false;
+        Gear^.doStep := @doStepCakeDown;
+        exit
+        end;
+
     cakeStep(Gear);
 
     if Gear^.Tag = 0 then
@@ -3102,18 +2672,6 @@
         CakePoints[CakeI].y := Gear^.Y;
         Gear^.DirAngle := DxDy2Angle(tdx, tdy);
         end;
-
-    dec(Gear^.Health);
-    Gear^.Timer := Gear^.Health*10;
-    if Gear^.Health mod 100 = 0 then
-        Gear^.PortalCounter:= 0;
-    // This is not seconds, but at least it is *some* feedback
-    if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then
-        begin
-        FollowGear := Gear;
-        Gear^.RenderTimer := false;
-        Gear^.doStep := @doStepCakeDown
-        end
 end;
 
 procedure doStepCakeUp(Gear: PGear);
@@ -3693,10 +3251,6 @@
 
     Gear^.X := HHGear^.X;
     Gear^.Y := HHGear^.Y;
-    // For some reason I need to reapply followgear here, something else grabs it otherwise.
-    // This is probably not needed anymore
-    if not CurrentTeam^.ExtDriven then
-        FollowGear := HHGear;
 
     if not isUnderWater and hasBorder and ((HHGear^.X < _0)
     or (hwRound(HHGear^.X) > LAND_WIDTH)) then
@@ -4048,7 +3602,7 @@
             break;
 
         // don't port portals or other gear that wouldn't make sense
-        if (iterator^.Kind in [gtPortal, gtRope])
+        if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack])
         or (iterator^.PortalCounter > 32) then
             continue;
 
@@ -4163,7 +3717,7 @@
         // calc gear offset in portal normal vector direction
         noffs:= (nx * ox + ny * oy);
 
-        if isBullet and (hwRound(hwAbs(noffs)) >= Gear^.Radius) then
+        if isBullet and (noffs.Round >= Gear^.Radius) then
             continue;
 
         // avoid gravity related loops of not really moving gear
@@ -4330,23 +3884,6 @@
             iterator^.Friction  := iterator^.Y;
             end;
 
-        // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
-        iterator:= GearsList;
-        while iterator <> nil do
-            begin
-            if (iterator^.Kind <> gtPortal) and ((iterator^.Hedgehog <> CurrentHedgehog)
-            or ((iterator^.Message and gmAllStoppable) = 0)) then
-                    begin
-                    iterator^.Active:= true;
-                    if iterator^.dY.QWordValue = _0.QWordValue then
-                        iterator^.dY.isNegative:= false;
-                    iterator^.State:= iterator^.State or gstMoving;
-                    DeleteCI(iterator);
-                //inc(iterator^.dY.QWordValue,10);
-                    end;
-            iterator:= iterator^.NextGear
-            end;
-
         if Gear^.Health > 1 then
             dec(Gear^.Health);
     end;
@@ -4398,7 +3935,7 @@
         Gear^.State := Gear^.State and (not gstMoving);
         
         if (Land[y, x] and lfBouncy <> 0)
-        or not CalcSlopeTangent(Gear, x, y, tx, ty, 255)
+        or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255))
         or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain
             begin
             loadNewPortalBall(Gear, true);
@@ -4475,7 +4012,7 @@
 
             iterator := GearsList;
             while iterator <> nil do
-            begin
+                begin
                 if (iterator^.Kind = gtPortal) then
                     if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then
                         begin
@@ -4493,7 +4030,27 @@
                         end;
                 iterator^.PortalCounter:= 0;
                 iterator := iterator^.NextGear
-            end;
+                end;
+
+            if newPortal^.LinkedGear <> nil then
+                begin
+                // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
+                iterator:= GearsList;
+                while iterator <> nil do
+                    begin
+                    if not (iterator^.Kind in [gtPortal, gtAirAttack]) and ((iterator^.Hedgehog <> CurrentHedgehog)
+                    or ((iterator^.Message and gmAllStoppable) = 0)) then
+                            begin
+                            iterator^.Active:= true;
+                            if iterator^.dY.QWordValue = _0.QWordValue then
+                                iterator^.dY.isNegative:= false;
+                            iterator^.State:= iterator^.State or gstMoving;
+                            DeleteCI(iterator);
+                        //inc(iterator^.dY.QWordValue,10);
+                            end;
+                    iterator:= iterator^.NextGear
+                    end
+                end
             end;
     newPortal^.State := newPortal^.State and (not gstCollision);
     newPortal^.State := newPortal^.State or gstMoving;
@@ -4796,7 +4353,7 @@
         Gear^.Timer:= Gear^.Tag
         end;
 
-    if (Gear^.Health = 0) or (HHGear^.Damage <> 0) then
+    if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then
         begin
         DeleteGear(Gear);
         AfterAttack
@@ -4872,7 +4429,7 @@
         Gear^.Timer:= Gear^.Tag
         end;
 
-    if (Gear^.Health = 0) or (HHGear^.Damage <> 0) or ((HHGear^.Message and gmAttack) <> 0) then
+    if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then
         begin
         HHGear^.Message:= HHGear^.Message and (not gmAttack);
         DeleteGear(Gear);
@@ -5597,3 +5154,84 @@
     
 doStepFallingGear(Gear);
 end;
+
+procedure doStepCreeper(Gear: PGear);
+var hogs: PGearArrayS;
+    HHGear: PGear;
+    tdX: hwFloat;
+    dir: LongInt;
+begin
+doStepFallingGear(Gear);
+if Gear^.Timer > 0 then dec(Gear^.Timer);
+// creeper sleep phase
+if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit;
+
+if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear
+else HHGear:= nil;
+
+// creeper boom phase
+if (Gear^.State and gstTmpFlag <> 0) then
+    begin
+    if (Gear^.Timer = 0) then
+        begin
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound);
+        DeleteGear(Gear)
+        end;
+    // ssssss he essssscaped
+    if (Gear^.Timer > 250) and ((HHGear = nil) or 
+            (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) >  180) and
+            (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then
+        begin
+        Gear^.State:= Gear^.State and (not gstTmpFlag);
+        Gear^.Timer:= 0
+        end;
+    exit
+    end;
+
+// Search out a new target, as target seek time has expired, target is dead, target is out of range, or we didn't have a target
+if (HHGear = nil) or (Gear^.Timer = 0) or 
+   (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) >  Gear^.Angle) and
+        (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle)))
+    then
+    begin
+    hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle);
+    if hogs.size > 1 then
+        Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog
+    else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog
+    else Gear^.Hedgehog:= nil;
+    if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000;
+    exit
+    end;
+
+// we have a target. move the creeper.
+if HHGear <> nil then
+    begin
+    // GOTCHA
+    if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) <  50) and
+         (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then
+        begin
+        // hisssssssssss
+        Gear^.State:= Gear^.State or gstTmpFlag;
+        Gear^.Timer:= 1500;
+        exit
+        end;
+    if (Gear^.State and gstMoving <> 0) then
+        begin
+        Gear^.dY:= _0;
+        Gear^.dX:= _0;
+        end
+    else if (GameTicks and $FF = 0) then
+        begin
+        tdX:= HHGear^.X-Gear^.X;
+        dir:= hwSign(tdX);
+        if not TestCollisionX(Gear, dir) then
+            Gear^.X:= Gear^.X + signAs(_1,tdX);
+        if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then
+            begin
+            Gear^.dX:= SignAs(_0_15, tdX);
+            Gear^.dY:= -_0_3;
+            Gear^.State:= Gear^.State or gstMoving
+            end
+        end;
+    end;
+end;
--- a/hedgewars/SDLh.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/SDLh.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -410,12 +410,10 @@
 
     PSDL_Color = ^TSDL_Color;
     TSDL_Color = record
-        case Byte of
-            0: ( r: Byte;
-                 g: Byte;
-                 b: Byte;
-                 unused: Byte; );
-            1: ( value: LongWord; );
+            r: Byte;
+            g: Byte;
+            b: Byte;
+            unused: Byte;
         end;
 
 
@@ -829,6 +827,8 @@
     TMixMusic = record
                 end;
 
+    TPostMix = procedure(udata: pointer; stream: PByte; len: LongInt); cdecl;
+
     {* SDL_net *}
     TIPAddress = record
                 host: LongWord;
@@ -959,6 +959,9 @@
 function  SDL_GL_SetAttribute(attr: TSDL_GLattr; value: LongInt): LongInt; cdecl; external SDLLibName;
 procedure SDL_GL_SwapBuffers; cdecl; external SDLLibName;
 
+procedure SDL_LockAudio; cdecl; external SDLLibName;
+procedure SDL_UnlockAudio; cdecl; external SDLLibName;
+
 function  SDL_NumJoysticks: LongInt; cdecl; external SDLLibName;
 function  SDL_JoystickName(idx: LongInt): PChar; cdecl; external SDLLibName;
 function  SDL_JoystickOpen(idx: LongInt): PSDL_Joystick; cdecl; external SDLLibName;
@@ -1010,6 +1013,7 @@
 
 function  Mix_OpenAudio(frequency: LongInt; format: Word; channels: LongInt; chunksize: LongInt): LongInt; cdecl; external SDL_MixerLibName;
 procedure Mix_CloseAudio; cdecl; external SDL_MixerLibName;
+function  Mix_QuerySpec(frequency: PLongInt; format: PWord; channels: PLongInt): LongInt; cdecl; external SDL_MixerLibName;
 
 function  Mix_Volume(channel: LongInt; volume: LongInt): LongInt; cdecl; external SDL_MixerLibName;
 function  Mix_SetDistance(channel: LongInt; distance: Byte): LongInt; cdecl; external SDL_MixerLibName;
@@ -1037,6 +1041,8 @@
 function  Mix_FadeInChannelTimed(channel: LongInt; chunk: PMixChunk; loops: LongInt; fadems: LongInt; ticks: LongInt): LongInt; cdecl; external SDL_MixerLibName;
 function  Mix_FadeOutChannel(channel: LongInt; fadems: LongInt): LongInt; cdecl; external SDL_MixerLibName;
 
+procedure Mix_SetPostMix( mix_func: TPostMix; arg: pointer); cdecl; external SDL_MixerLibName;
+
 (*  SDL_image  *)
 function  IMG_Init(flags: LongInt): LongInt; {$IFDEF SDL_IMAGE_NEWER}cdecl; external SDL_ImageLibName;{$ENDIF}
 procedure IMG_Quit; {$IFDEF SDL_IMAGE_NEWER}cdecl; external SDL_ImageLibName;{$ENDIF}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/avwrapper.c	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,515 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include "libavformat/avformat.h"
+
+#ifndef AVIO_FLAG_WRITE
+#define AVIO_FLAG_WRITE AVIO_WRONLY
+#endif
+
+static AVFormatContext* g_pContainer;
+static AVOutputFormat* g_pFormat;
+static AVStream* g_pAStream;
+static AVStream* g_pVStream;
+static AVFrame* g_pAFrame;
+static AVFrame* g_pVFrame;
+static AVCodec* g_pACodec;
+static AVCodec* g_pVCodec;
+static AVCodecContext* g_pAudio;
+static AVCodecContext* g_pVideo;
+
+static int g_Width, g_Height;
+static uint32_t g_Frequency, g_Channels;
+static int g_VQuality;
+static AVRational g_Framerate;
+
+static FILE* g_pSoundFile;
+static int16_t* g_pSamples;
+static int g_NumSamples;
+
+/*
+Initially I wrote code for latest ffmpeg, but on Linux (Ubuntu)
+only older version is available from repository. That's why you see here
+all of this #if LIBAVCODEC_VERSION_MAJOR < 54.
+Actually, it may be possible to remove code for newer version
+and use only code for older version.
+*/
+
+#if LIBAVCODEC_VERSION_MAJOR < 54
+#define OUTBUFFER_SIZE 200000
+static uint8_t g_OutBuffer[OUTBUFFER_SIZE];
+#endif
+
+// pointer to function from hwengine (uUtils.pas)
+static void (*AddFileLogRaw)(const char* pString);
+
+static void FatalError(const char* pFmt, ...)
+{
+    const char Buffer[1024];
+    va_list VaArgs;
+
+    va_start(VaArgs, pFmt);
+    vsnprintf(Buffer, 1024, pFmt, VaArgs);
+    va_end(VaArgs);
+
+    AddFileLogRaw("Error in av-wrapper: ");
+    AddFileLogRaw(Buffer);
+    AddFileLogRaw("\n");
+    exit(1);
+}
+
+// Function to be called from libav for logging.
+// Note: libav can call LogCallback from different threads
+// (there is mutex in AddFileLogRaw).
+static void LogCallback(void* p, int Level, const char* pFmt, va_list VaArgs)
+{
+    const char Buffer[1024];
+
+    vsnprintf(Buffer, 1024, pFmt, VaArgs);
+    AddFileLogRaw(Buffer);
+}
+
+static void Log(const char* pFmt, ...)
+{
+    const char Buffer[1024];
+    va_list VaArgs;
+
+    va_start(VaArgs, pFmt);
+    vsnprintf(Buffer, 1024, pFmt, VaArgs);
+    va_end(VaArgs);
+
+    AddFileLogRaw(Buffer);
+}
+
+static void AddAudioStream()
+{
+#if LIBAVFORMAT_VERSION_MAJOR >= 54
+    g_pAStream = avformat_new_stream(g_pContainer, g_pACodec);
+#else
+    g_pAStream = av_new_stream(g_pContainer, 1);
+#endif
+    if(!g_pAStream)
+    {
+        Log("Could not allocate audio stream\n");
+        return;
+    }
+    g_pAStream->id = 1;
+
+    g_pAudio = g_pAStream->codec;
+
+    avcodec_get_context_defaults3(g_pAudio, g_pACodec);
+    g_pAudio->codec_id = g_pACodec->id;
+
+    // put parameters
+    g_pAudio->sample_fmt = AV_SAMPLE_FMT_S16;
+    g_pAudio->sample_rate = g_Frequency;
+    g_pAudio->channels = g_Channels;
+
+    // set quality
+    g_pAudio->bit_rate = 160000;
+
+    // for codecs that support variable bitrate use it, it should be better
+    g_pAudio->flags |= CODEC_FLAG_QSCALE;
+    g_pAudio->global_quality = 1*FF_QP2LAMBDA;
+
+    // some formats want stream headers to be separate
+    if (g_pFormat->flags & AVFMT_GLOBALHEADER)
+        g_pAudio->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+    // open it
+#if LIBAVCODEC_VERSION_MAJOR >= 53
+    if (avcodec_open2(g_pAudio, g_pACodec, NULL) < 0)
+#else
+    if (avcodec_open(g_pAudio, g_pACodec) < 0)
+#endif
+    {
+        Log("Could not open audio codec %s\n", g_pACodec->long_name);
+        return;
+    }
+
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+    if (g_pACodec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)
+#else
+    if (g_pAudio->frame_size == 0)
+#endif
+        g_NumSamples = 4096;
+    else
+        g_NumSamples = g_pAudio->frame_size;
+    g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t));
+    g_pAFrame = avcodec_alloc_frame();
+    if (!g_pAFrame)
+    {
+        Log("Could not allocate frame\n");
+        return;
+    }
+}
+
+// returns non-zero if there is more sound
+static int WriteAudioFrame()
+{
+    if (!g_pAStream)
+        return 0;
+
+    AVPacket Packet = { 0 };
+    av_init_packet(&Packet);
+
+    int NumSamples = fread(g_pSamples, 2*g_Channels, g_NumSamples, g_pSoundFile);
+
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+    AVFrame* pFrame = NULL;
+    if (NumSamples > 0)
+    {
+        g_pAFrame->nb_samples = NumSamples;
+        avcodec_fill_audio_frame(g_pAFrame, g_Channels, AV_SAMPLE_FMT_S16,
+                                 (uint8_t*)g_pSamples, NumSamples*2*g_Channels, 1);
+        pFrame = g_pAFrame;
+    }
+    // when NumSamples == 0 we still need to call encode_audio2 to flush
+    int got_packet;
+    if (avcodec_encode_audio2(g_pAudio, &Packet, pFrame, &got_packet) != 0)
+        FatalError("avcodec_encode_audio2 failed");
+    if (!got_packet)
+        return 0;
+#else
+    if (NumSamples == 0)
+        return 0;
+    int BufferSize = OUTBUFFER_SIZE;
+    if (g_pAudio->frame_size == 0)
+        BufferSize = NumSamples*g_Channels*2;
+    Packet.size = avcodec_encode_audio(g_pAudio, g_OutBuffer, BufferSize, g_pSamples);
+    if (Packet.size == 0)
+        return 1;
+    if (g_pAudio->coded_frame && g_pAudio->coded_frame->pts != AV_NOPTS_VALUE)
+        Packet.pts = av_rescale_q(g_pAudio->coded_frame->pts, g_pAudio->time_base, g_pAStream->time_base);
+    Packet.flags |= AV_PKT_FLAG_KEY;
+    Packet.data = g_OutBuffer;
+#endif
+
+    // Write the compressed frame to the media file.
+    Packet.stream_index = g_pAStream->index;
+    if (av_interleaved_write_frame(g_pContainer, &Packet) != 0) 
+        FatalError("Error while writing audio frame");
+    return 1;
+}
+
+// add a video output stream
+static void AddVideoStream()
+{
+#if LIBAVFORMAT_VERSION_MAJOR >= 54
+    g_pVStream = avformat_new_stream(g_pContainer, g_pVCodec);
+#else
+    g_pVStream = av_new_stream(g_pContainer, 0);
+#endif
+    if (!g_pVStream)
+        FatalError("Could not allocate video stream");
+
+    g_pVideo = g_pVStream->codec;
+
+    avcodec_get_context_defaults3(g_pVideo, g_pVCodec);
+    g_pVideo->codec_id = g_pVCodec->id;
+
+    // put parameters
+    // resolution must be a multiple of two
+    g_pVideo->width  = g_Width  & ~1; // make even (dimensions should be even)
+    g_pVideo->height = g_Height & ~1; // make even
+    /* time base: this is the fundamental unit of time (in seconds) in terms
+       of which frame timestamps are represented. for fixed-fps content,
+       timebase should be 1/framerate and timestamp increments should be
+       identically 1. */
+    g_pVideo->time_base.den = g_Framerate.num;
+    g_pVideo->time_base.num = g_Framerate.den;
+    //g_pVideo->gop_size = 12; /* emit one intra frame every twelve frames at most */
+    g_pVideo->pix_fmt = PIX_FMT_YUV420P;
+
+    // set quality
+    if (g_VQuality > 100)
+        g_pVideo->bit_rate = g_VQuality;
+    else
+    {
+        g_pVideo->flags |= CODEC_FLAG_QSCALE;
+        g_pVideo->global_quality = g_VQuality*FF_QP2LAMBDA;
+    }
+
+    // some formats want stream headers to be separate
+    if (g_pFormat->flags & AVFMT_GLOBALHEADER)
+        g_pVideo->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+#if LIBAVCODEC_VERSION_MAJOR < 54
+    // for some versions of ffmpeg x264 options must be set explicitly
+    if (strcmp(g_pVCodec->name, "libx264") == 0)
+    {
+        g_pVideo->coder_type = FF_CODER_TYPE_AC;
+        g_pVideo->flags |= CODEC_FLAG_LOOP_FILTER;
+        g_pVideo->crf = 23;
+        g_pVideo->thread_count = 3;
+        g_pVideo->me_cmp = FF_CMP_CHROMA;
+        g_pVideo->partitions = X264_PART_I8X8 | X264_PART_I4X4 | X264_PART_P8X8 | X264_PART_B8X8;
+        g_pVideo->me_method = ME_HEX;
+        g_pVideo->me_subpel_quality = 7;
+        g_pVideo->me_range = 16;
+        g_pVideo->gop_size = 250;
+        g_pVideo->keyint_min = 25;
+        g_pVideo->scenechange_threshold = 40;
+        g_pVideo->i_quant_factor = 0.71;
+        g_pVideo->b_frame_strategy = 1;
+        g_pVideo->qcompress = 0.6;
+        g_pVideo->qmin = 10;
+        g_pVideo->qmax = 51;
+        g_pVideo->max_qdiff = 4;
+        g_pVideo->max_b_frames = 3;
+        g_pVideo->refs = 3;
+        g_pVideo->directpred = 1;
+        g_pVideo->trellis = 1;
+        g_pVideo->flags2 = CODEC_FLAG2_BPYRAMID | CODEC_FLAG2_MIXED_REFS | CODEC_FLAG2_WPRED | CODEC_FLAG2_8X8DCT | CODEC_FLAG2_FASTPSKIP;
+        g_pVideo->weighted_p_pred = 2;
+    }
+#endif
+
+    // open the codec
+#if LIBAVCODEC_VERSION_MAJOR >= 53
+    AVDictionary* pDict = NULL;
+    if (strcmp(g_pVCodec->name, "libx264") == 0)
+        av_dict_set(&pDict, "preset", "medium", 0);
+
+    if (avcodec_open2(g_pVideo, g_pVCodec, &pDict) < 0)
+#else
+    if (avcodec_open(g_pVideo, g_pVCodec) < 0)
+#endif
+        FatalError("Could not open video codec %s", g_pVCodec->long_name);
+
+    g_pVFrame = avcodec_alloc_frame();
+    if (!g_pVFrame)
+        FatalError("Could not allocate frame");
+
+    g_pVFrame->linesize[0] = g_Width;
+    g_pVFrame->linesize[1] = g_Width/2;
+    g_pVFrame->linesize[2] = g_Width/2;
+    g_pVFrame->linesize[3] = 0;
+}
+
+static int WriteFrame(AVFrame* pFrame)
+{
+    double AudioTime, VideoTime;
+
+    // write interleaved audio frame
+    if (g_pAStream)
+    {
+        VideoTime = (double)g_pVStream->pts.val*g_pVStream->time_base.num/g_pVStream->time_base.den;
+        do
+            AudioTime = (double)g_pAStream->pts.val*g_pAStream->time_base.num/g_pAStream->time_base.den;
+        while (AudioTime < VideoTime && WriteAudioFrame());
+    }
+    
+    if (!g_pVStream)
+        return 0;
+
+    AVPacket Packet;
+    av_init_packet(&Packet);
+    Packet.data = NULL;
+    Packet.size = 0;
+
+    g_pVFrame->pts++;
+    if (g_pFormat->flags & AVFMT_RAWPICTURE)
+    {
+        /* raw video case. The API will change slightly in the near
+           future for that. */
+        Packet.flags |= AV_PKT_FLAG_KEY;
+        Packet.stream_index = g_pVStream->index;
+        Packet.data = (uint8_t*)pFrame;
+        Packet.size = sizeof(AVPicture);
+
+        if (av_interleaved_write_frame(g_pContainer, &Packet) != 0)
+            FatalError("Error while writing video frame");
+        return 0;
+    }
+    else
+    {
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+        int got_packet;
+        if (avcodec_encode_video2(g_pVideo, &Packet, pFrame, &got_packet) < 0)
+            FatalError("avcodec_encode_video2 failed");
+        if (!got_packet)
+            return 0;
+
+        if (Packet.pts != AV_NOPTS_VALUE)
+            Packet.pts = av_rescale_q(Packet.pts, g_pVideo->time_base, g_pVStream->time_base);
+        if (Packet.dts != AV_NOPTS_VALUE)
+            Packet.dts = av_rescale_q(Packet.dts, g_pVideo->time_base, g_pVStream->time_base);
+#else 
+        Packet.size = avcodec_encode_video(g_pVideo, g_OutBuffer, OUTBUFFER_SIZE, pFrame);
+        if (Packet.size < 0)
+            FatalError("avcodec_encode_video failed");
+        if (Packet.size == 0)
+            return 0;
+
+        if( g_pVideo->coded_frame->pts != AV_NOPTS_VALUE)
+            Packet.pts = av_rescale_q(g_pVideo->coded_frame->pts, g_pVideo->time_base, g_pVStream->time_base);
+        if( g_pVideo->coded_frame->key_frame )
+            Packet.flags |= AV_PKT_FLAG_KEY;
+        Packet.data = g_OutBuffer;
+#endif
+        // write the compressed frame in the media file
+        Packet.stream_index = g_pVStream->index;
+        if (av_interleaved_write_frame(g_pContainer, &Packet) != 0)
+            FatalError("Error while writing video frame");
+            
+        return 1;
+    }
+}
+
+void AVWrapper_WriteFrame(uint8_t* pY, uint8_t* pCb, uint8_t* pCr)
+{
+    g_pVFrame->data[0] = pY;
+    g_pVFrame->data[1] = pCb;
+    g_pVFrame->data[2] = pCr;
+    WriteFrame(g_pVFrame);
+}
+
+void AVWrapper_Init(
+         void (*pAddFileLogRaw)(const char*),
+         const char* pFilename,
+         const char* pDesc,
+         const char* pSoundFile,
+         const char* pFormatName,
+         const char* pVCodecName,
+         const char* pACodecName,
+         int Width, int Height,
+         int FramerateNum, int FramerateDen,
+         int VQuality)
+{    
+    AddFileLogRaw = pAddFileLogRaw;
+    av_log_set_callback( &LogCallback );
+
+    g_Width  = Width;
+    g_Height = Height;
+    g_Framerate.num = FramerateNum;
+    g_Framerate.den = FramerateDen;
+    g_VQuality = VQuality;
+
+    // initialize libav and register all codecs and formats
+    av_register_all();
+
+    // find format
+    g_pFormat = av_guess_format(pFormatName, NULL, NULL);
+    if (!g_pFormat)
+        FatalError("Format \"%s\" was not found", pFormatName);
+
+    // allocate the output media context
+    g_pContainer = avformat_alloc_context();
+    if (!g_pContainer)
+        FatalError("Could not allocate output context");
+
+    g_pContainer->oformat = g_pFormat;
+
+    // store description of file
+    av_dict_set(&g_pContainer->metadata, "comment", pDesc, 0);
+
+    // append extesnion to filename
+    char ext[16];
+    strncpy(ext, g_pFormat->extensions, 16);
+    ext[15] = 0;
+    ext[strcspn(ext,",")] = 0;
+    snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), "%s.%s", pFilename, ext);
+
+    // find codecs
+    g_pVCodec = avcodec_find_encoder_by_name(pVCodecName);
+    g_pACodec = avcodec_find_encoder_by_name(pACodecName);
+
+    // add audio and video stream to container
+    g_pVStream = NULL;
+    g_pAStream = NULL;
+
+    if (g_pVCodec)
+        AddVideoStream();
+    else
+        Log("Video codec \"%s\" was not found; video will be ignored.\n", pVCodecName);
+
+    if (g_pACodec)
+    {
+        g_pSoundFile = fopen(pSoundFile, "rb");
+        if (g_pSoundFile)
+        {
+            fread(&g_Frequency, 4, 1, g_pSoundFile);
+            fread(&g_Channels, 4, 1, g_pSoundFile);
+            AddAudioStream();
+        }
+        else
+            Log("Could not open %s\n", pSoundFile);
+    }
+    else
+        Log("Audio codec \"%s\" was not found; audio will be ignored.\n", pACodecName);
+
+    if (!g_pAStream && !g_pVStream)
+        FatalError("No video, no audio, aborting...");
+
+    // write format info to log
+    av_dump_format(g_pContainer, 0, g_pContainer->filename, 1);
+
+    // open the output file, if needed
+    if (!(g_pFormat->flags & AVFMT_NOFILE))
+    {
+        if (avio_open(&g_pContainer->pb, g_pContainer->filename, AVIO_FLAG_WRITE) < 0)
+            FatalError("Could not open output file (%s)", g_pContainer->filename);
+    }
+
+    // write the stream header, if any
+    avformat_write_header(g_pContainer, NULL);
+
+    g_pVFrame->pts = -1;
+}
+
+void AVWrapper_Close()
+{
+    // output buffered frames
+    if (g_pVCodec->capabilities & CODEC_CAP_DELAY)
+        while( WriteFrame(NULL) );
+    // output any remaining audio
+    while( WriteAudioFrame() );
+
+    // write the trailer, if any.
+    av_write_trailer(g_pContainer);
+
+    // close the output file
+    if (!(g_pFormat->flags & AVFMT_NOFILE))
+        avio_close(g_pContainer->pb);
+
+    // free everything
+    if (g_pVStream)
+    {
+        avcodec_close(g_pVideo);
+        av_free(g_pVideo);
+        av_free(g_pVStream);
+        av_free(g_pVFrame);
+    }
+    if (g_pAStream)
+    {
+        avcodec_close(g_pAudio);
+        av_free(g_pAudio);
+        av_free(g_pAStream);
+        av_free(g_pAFrame);
+        av_free(g_pSamples);
+        fclose(g_pSoundFile);
+    }
+
+    av_free(g_pContainer);
+}
--- a/hedgewars/hwengine.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/hwengine.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -30,9 +30,11 @@
 {$ENDIF}
 
 uses SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler, uSound,
-     uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uRandom, uLandTexture, uCollisions,
+     uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uLandTexture, uCollisions,
      SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted
-     {$IFDEF SDL13}, uTouch{$ENDIF}{$IFDEF ANDROID}, GLUnit{$ENDIF};
+     {$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF}
+     {$IFDEF SDL13}, uTouch{$ENDIF}{$IFDEF ANDROID}, GLUnit{$ENDIF}, uAILandMarks;
+
 
 {$IFDEF HWLIBRARY}
 procedure initEverything(complete:boolean);
@@ -57,6 +59,9 @@
         gsLandGen:
             begin
             GenMap;
+            uLandTexture.initModule;
+            UpdateLandTexture(0, LAND_WIDTH, 0, LAND_HEIGHT, false); 
+            uAILandMarks.initModule;
             ParseCommand('sendlanddigest', true);
             GameState:= gsStart;
             end;
@@ -81,7 +86,7 @@
             end;
         gsConfirm, gsGame:
             begin
-            DrawWorld(Lag); // never place between ProcessKbd and DoGameTick - bugs due to /put cmd and isCursorVisible
+            DrawWorld(Lag);
             DoGameTick(Lag);
             ProcessVisualGears(Lag);
             end;
@@ -101,18 +106,27 @@
 
     SwapBuffers;
 
+{$IFDEF USE_VIDEO_RECORDING}
+    if flagPrerecording then
+        SaveCameraPosition;
+{$ENDIF}
+
     if flagMakeCapture then
         begin
         flagMakeCapture:= false;
         {$IFDEF PAS2C}
-        s:= 'hw';
+        s:= '/Screenshots/hw';
         {$ELSE}
-        s:= 'hw_' + FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now()) + inttostr(GameTicks);
+        s:= '/Screenshots/hw_' + FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now()) + inttostr(GameTicks);
         {$ENDIF}
 
+        // flash
         playSound(sndShutter);
-        
-        if MakeScreenshot(s) then
+        ScreenFade:= sfFromWhite;
+        ScreenFadeValue:= sfMax;
+        ScreenFadeSpeed:= 5;
+
+        if MakeScreenshot(s, 1) then
             WriteLnToConsole('Screenshot saved: ' + s)
         else
             begin
@@ -261,6 +275,39 @@
     end;
 end;
 
+{$IFDEF USE_VIDEO_RECORDING}
+procedure RecorderMainLoop;
+var oldGameTicks, oldRealTicks, newGameTicks, newRealTicks: LongInt;
+begin
+    if not BeginVideoRecording() then
+        exit;
+    DoTimer(0); // gsLandGen -> gsStart
+    DoTimer(0); // gsStart -> gsGame
+
+    if not LoadNextCameraPosition(newRealTicks, newGameTicks) then
+        exit;
+    fastScrolling:= true;
+    DoGameTick(newGameTicks);
+    fastScrolling:= false;
+    oldRealTicks:= 0;
+    oldGameTicks:= newGameTicks;
+
+    while LoadNextCameraPosition(newRealTicks, newGameTicks) do
+    begin
+        IPCCheckSock();
+        DoGameTick(newGameTicks - oldGameTicks);
+        if GameState = gsExit then
+            break;
+        ProcessVisualGears(newRealTicks - oldRealTicks);
+        DrawWorld(newRealTicks - oldRealTicks);
+        EncodeFrame();
+        oldRealTicks:= newRealTicks;
+        oldGameTicks:= newGameTicks;
+    end;
+    StopVideoRecording();
+end;
+{$ENDIF}
+
 ///////////////
 procedure Game{$IFDEF HWLIBRARY}(gameArgs: PPChar); cdecl; export{$ENDIF};
 var p: TPathType;
@@ -327,11 +374,18 @@
     SDLTry(TTF_Init() <> -1, true);
     WriteLnToConsole(msgOK);
 
-    // show main window
-    if cFullScreen then
-        ParseCommand('fullscr 1', true)
+{$IFDEF USE_VIDEO_RECORDING}
+    if GameType = gmtRecord then
+        InitOffscreenOpenGL()
     else
-        ParseCommand('fullscr 0', true);
+{$ENDIF}
+        begin            
+        // show main window
+        if cFullScreen then
+            ParseCommand('fullscr 1', true)
+        else
+            ParseCommand('fullscr 0', true);
+        end;
 
     ControllerInit(); // has to happen before InitKbdKeyTable to map keys
     InitKbdKeyTable();
@@ -368,12 +422,22 @@
 
     InitTeams();
     AssignStores();
+
+    if GameType = gmtRecord then
+        SetSound(false);
+
     InitSound();
 
     isDeveloperMode:= false;
     TryDo(InitStepsFlags = cifAllInited, 'Some parameters not set (flags = ' + inttostr(InitStepsFlags) + ')', true);
     ParseCommand('rotmask', true);
-    MainLoop();
+
+{$IFDEF USE_VIDEO_RECORDING}
+    if GameType = gmtRecord then
+        RecorderMainLoop()
+    else
+{$ENDIF}
+        MainLoop();
 
     // clean up all the memory allocated
     freeEverything(true);
@@ -412,9 +476,7 @@
         //uLandGraphics does not need initialization
         //uLandObjects does not need initialization
         //uLandTemplates does not need initialization
-        uLandTexture.initModule;
         //uLocale does not need initialization
-        uRandom.initModule;
         uScript.initModule;
         uSound.initModule;
         uStats.initModule;
@@ -432,6 +494,7 @@
     begin
         WriteLnToConsole('Freeing resources...');
         uAI.freeModule;
+        uAILandMarks.freeModule;
         uAIMisc.freeModule;         //stub
         uCaptions.freeModule;
         uWorld.freeModule;
@@ -441,7 +504,7 @@
         uStats.freeModule;          //stub
         uSound.freeModule;
         uScript.freeModule;
-        uRandom.freeModule;         //stub
+        //uRandom does not need to be freed
         //uLocale does not need to be freed
         //uLandTemplates does not need to be freed
         uLandTexture.freeModule;
@@ -456,6 +519,7 @@
         //uAIAmmoTests does not need to be freed
         //uAIActions does not need to be freed
         uStore.freeModule;
+{$IFDEF USE_VIDEO_RECORDING}uVideoRec.freeModule;{$ENDIF}
     end;
 
     uIO.freeModule;
@@ -529,11 +593,14 @@
     else
         if (ParamCount = 3) and ((ParamStr(3) = '--stats-only') or (ParamStr(3) = 'landpreview')) then
             internalSetGameTypeLandPreviewFromParameters()
+        else if ParamCount = cDefaultParamNum then
+            internalStartGameWithParameters()
+{$IFDEF USE_VIDEO_RECORDING}
+        else if ParamCount = cVideorecParamNum then
+            internalStartVideoRecordingWithParameters()
+{$ENDIF}
         else
-            if (ParamCount = cDefaultParamNum) then
-                internalStartGameWithParameters()
-            else
-                playReplayFileWithParameters();
+            playReplayFileWithParameters();
 end;
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/hedgewars/pas2cSystem.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/pas2cSystem.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -38,6 +38,7 @@
     PPChar = ^Pchar;
     
     PByte = ^Byte;
+    PWord = ^Word;
     PLongInt = ^LongInt;
     PLongWord = ^LongWord;
     PInteger = ^Integer;
--- a/hedgewars/uAI.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAI.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -31,7 +31,7 @@
 implementation
 uses uConsts, SDLh, uAIMisc, uAIAmmoTests, uAIActions,
     uAmmos, SysUtils{$IFNDEF USE_SDLTHREADS} {$IFDEF UNIX}, cthreads{$ENDIF} {$ENDIF}, uTypes,
-    uVariables, uCommands, uUtils, uDebug;
+    uVariables, uCommands, uUtils, uDebug, uAILandMarks;
 
 var BestActions: TActions;
     CanUseAmmo: array [TAmmoType] of boolean;
@@ -178,9 +178,12 @@
                         begin
                         AddAction(BestActions, aia_attack, aim_push, 350 + random(200), 0, 0);
                         AddAction(BestActions, aia_attack, aim_release, 1, 0, 0);
-                                                
-                        AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0);
-                        AddAction(BestActions, aia_Down, aim_release, 32, 0, 0);
+                         
+                        if abs(ap.Angle) > 32 then
+                           begin
+                           AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0);
+                           AddAction(BestActions, aia_Down, aim_release, 32, 0, 0);
+                           end;
                         
                         AddAction(BestActions, aia_waitAngle, ap.Angle, 250, 0, 0);
                         AddAction(BestActions, aia_attack, aim_push, 1, 0, 0);
@@ -191,7 +194,14 @@
                             AddAction(BestActions, aia_attack, aim_push, 650 + random(300), 0, 0);
                             AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0);
                             end;
-                            
+
+                    if (Ammoz[a].Ammo.Propz and ammoprop_Track) <> 0 then
+                        begin
+                        AddAction(BestActions, aia_waitAmmoXY, 0, 12, ap.ExplX, ap.ExplY);
+                        AddAction(BestActions, aia_attack, aim_push, 1, 0, 0);
+                        AddAction(BestActions, aia_attack, aim_release, 7, 0, 0);
+                        end;
+
                     if ap.ExplR > 0 then
                         AddAction(BestActions, aia_AwareExpl, ap.ExplR, 10, ap.ExplX, ap.ExplY);
                     end
@@ -205,7 +215,7 @@
 end;
 
 procedure Walk(Me: PGear; var Actions: TActions);
-const FallPixForBranching = cHHRadius * 2 + 8;
+const FallPixForBranching = cHHRadius;
 var
     ticks, maxticks, steps, tmp: Longword;
     BaseRate, BestRate, Rate: integer;
@@ -267,7 +277,9 @@
                 break;
 
             if (BotLevel < 5) and (GoInfo.JumpType = jmpHJump) then // hjump support
-                if Push(ticks, Actions, AltMe, Me^.Message) then
+                // check if we could go backwards and maybe ljump over a gap after this hjump
+                if Push(ticks, Actions, AltMe, Me^.Message xor 3) then
+                    begin
                     with Stack.States[Pred(Stack.Count)] do
                         begin
                         if Me^.dX.isNegative then
@@ -283,11 +295,20 @@
                         else
                             AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0);
                         end;
+                    // but first check walking forward
+                    Push(ticks, Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message)
+                    end;
             if (BotLevel < 3) and (GoInfo.JumpType = jmpLJump) then // ljump support
                 begin
-                // push current position so we proceed from it after checking jump opportunities
+                // at final check where we go after jump walking backward
+                if Push(ticks, Actions, AltMe, Me^.Message xor 3) then
+                    with Stack.States[Pred(Stack.Count)] do
+                        AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0);
+
+                // push current position so we proceed from it after checking jump+forward walk opportunities
                 if CanGo then Push(ticks, Actions, Me^, Me^.Message);
-                // first check where we go after jump
+                
+                // first check where we go after jump walking forward
                 if Push(ticks, Actions, AltMe, Me^.Message) then
                     with Stack.States[Pred(Stack.Count)] do
                         AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0);
@@ -310,8 +331,16 @@
                 end
             else if Rate < BestRate then
                 break;
+                
             if ((Me^.State and gstAttacked) = 0) and ((steps mod 4) = 0) then
+                begin
+                if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWasHere) then
+                    break;                    
+                addMark(hwRound(Me^.X), hwRound(Me^.Y), markWasHere);
+                
                 TestAmmos(Actions, Me, true);
+                end;
+                
             if GoInfo.FallPix >= FallPixForBranching then
                 Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right
             end {while};
@@ -408,7 +437,7 @@
         end
     end;
 
-PGear(Me)^.State:= PGear(Me)^.State and not gstHHThinking;
+PGear(Me)^.State:= PGear(Me)^.State and (not gstHHThinking);
 Think:= 0;
 InterlockedDecrement(hasThread)
 end;
@@ -419,7 +448,9 @@
 or isInMultiShoot then
     exit;
 
-//DeleteCI(Me); // this might break demo
+//DeleteCI(Me); // this will break demo/netplay
+clearAllMarks;
+
 Me^.State:= Me^.State or gstHHThinking;
 Me^.Message:= 0;
 
@@ -476,12 +507,11 @@
                 
             end else
                 begin
-                (*
-                if not scoreShown then
+                {if not scoreShown then
                     begin
                     if BestActions.Score > 0 then ParseCommand('/say Expected score = ' + inttostr(BestActions.Score div 1024), true);
                     scoreShown:= true
-                    end;*)
+                    end;}
                 ProcessAction(BestActions, Gear)
                 end
         else if ((GameTicks - StartTicks) > cMaxAIThinkTime)
--- a/hedgewars/uAIActions.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAIActions.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -44,6 +44,7 @@
     aia_Wait       = $8009;
     aia_Put        = $800A;
     aia_waitAngle  = $800B;
+    aia_waitAmmoXY = $800C;
     
     aim_push       = $8000;
     aim_release    = $8001;
@@ -115,19 +116,19 @@
 
 procedure AddAction(var Actions: TActions; Action: Longword; Param: LongInt; TimeDelta: Longword; X, Y: LongInt);
 begin
-with Actions do
-    begin
-    actions[Count].Action:= Action;
-    actions[Count].Param:= Param;
-    actions[Count].X:= X;
-    actions[Count].Y:= Y;
-    if Count > 0 then
-        actions[Count].Time:= TimeDelta
-    else
-        actions[Count].Time:= GameTicks + TimeDelta;
-    inc(Count);
-    TryDo(Count < MAXACTIONS, 'AI: actions overflow', true);
-    end
+if Actions.Count < MAXACTIONS then
+    with Actions do
+        begin
+        actions[Count].Action:= Action;
+        actions[Count].Param:= Param;
+        actions[Count].X:= X;
+        actions[Count].Y:= Y;
+        if Count > 0 then
+            actions[Count].Time:= TimeDelta
+        else
+            actions[Count].Time:= GameTicks + TimeDelta;
+        inc(Count);
+        end
 end;
 
 procedure CheckHang(Me: PGear);
@@ -234,6 +235,10 @@
                 
             aia_waitAngle:
                 if Me^.Angle <> Abs(Param) then exit;
+
+            aia_waitAmmoXY:
+                if (CurAmmoGear <> nil) and ((hwRound(CurAmmoGear^.X) <> X) or (hwRound(CurAmmoGear^.Y) <> Y)) then exit;
+
             end
         else
             begin
--- a/hedgewars/uAIAmmoTests.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAIAmmoTests.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -47,6 +47,7 @@
 function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestWhip(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestKamikaze(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestAirAttack(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestTeleport(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestHammer(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
@@ -84,8 +85,8 @@
             //(proc: @TestTeleport;    flags: amtest_OnTurn), // amTeleport
             (proc: nil;              flags: 0), // amSwitch
             (proc: @TestMortar;      flags: 0), // amMortar
-            (proc: nil;              flags: 0), // amKamikaze
-            (proc: @TestCake;        flags: 0), // amCake
+            (proc: @TestKamikaze;    flags: 0), // amKamikaze
+            (proc: @TestCake;        flags: amtest_OnTurn or amtest_NoTarget), // amCake
             (proc: nil;              flags: 0), // amSeduction
             (proc: @TestWatermelon;  flags: 0), // amWatermelon
             (proc: nil;              flags: 0), // amHellishBomb
@@ -122,7 +123,7 @@
 const BadTurn = Low(LongInt) div 4;
 
 implementation
-uses uAIMisc, uVariables, uUtils;
+uses uAIMisc, uVariables, uUtils, uGearsHandlers, uCollisions;
 
 function Metric(x1, y1, x2, y2: LongInt): LongInt; inline;
 begin
@@ -167,7 +168,7 @@
         
         EX:= trunc(x);
         EY:= trunc(y);
-        if Me^.Hedgehog^.BotLevel = 1 then
+        if Level = 1 then
             value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
         else value:= RateExplosion(Me, EX, EY, 101);
         if value = 0 then
@@ -330,7 +331,7 @@
     EX:= trunc(x);
     EY:= trunc(y);
     if t < 50 then 
-        if Me^.Hedgehog^.BotLevel = 1 then
+        if Level = 1 then
             Score:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
         else Score:= RateExplosion(Me, EX, EY, 101)
     else 
@@ -361,12 +362,12 @@
     t: LongInt;
 begin
 valueResult:= BadTurn;
-TestTime:= 0;
+TestTime:= 500;
 ap.ExplR:= 0;
 meX:= hwFloat2Float(Me^.X);
 meY:= hwFloat2Float(Me^.Y);
 repeat
-    inc(TestTime, 1000);
+    inc(TestTime, 900);
     // Try to overshoot slightly, seems to pay slightly better dividends in terms of hitting cluster
     if meX<Targ.X then
         Vx:= ((Targ.X+10) - meX) / (TestTime + tDelta)
@@ -398,14 +399,14 @@
         begin
         ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level));
         ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15);
-        ap.Time:= TestTime;
+        ap.Time:= TestTime div 1000 * 1000;
         ap.ExplR:= 90;
         ap.ExplX:= EX;
         ap.ExplY:= EY;
         valueResult:= Score
         end;
      end
-until (TestTime = 4000);
+until (TestTime = 4100);
 TestClusterBomb:= valueResult
 end;
 
@@ -418,12 +419,12 @@
     t: LongInt;
 begin
 valueResult:= BadTurn;
-TestTime:= 0;
+TestTime:= 500;
 ap.ExplR:= 0;
 meX:= hwFloat2Float(Me^.X);
 meY:= hwFloat2Float(Me^.Y);
 repeat
-    inc(TestTime, 1000);
+    inc(TestTime, 900);
     Vx:= (Targ.X - meX) / (TestTime + tDelta);
     Vy:= cGravityf * ((TestTime + tDelta) div 2) - ((Targ.Y-50) - meY) / (TestTime + tDelta);
     r:= sqr(Vx)+sqr(Vy);
@@ -452,14 +453,14 @@
             begin
             ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level));
             ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15);
-            ap.Time:= TestTime;
+            ap.Time:= TestTime div 1000 * 1000;
             ap.ExplR:= 300;
             ap.ExplX:= EX;
             ap.ExplY:= EY;
             valueResult:= Score
             end;
         end
-until (TestTime = 4000);
+until (TestTime = 4100);
 TestWatermelon:= valueResult
 end;
 
@@ -600,19 +601,19 @@
     d: Longword;
     fallDmg, valueResult: LongInt;
 begin
-if Me^.Hedgehog^.BotLevel > 3 then exit(BadTurn);
+if Level > 3 then exit(BadTurn);
 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
 Level:= Level; // avoid compiler hint
 ap.ExplR:= 0;
 ap.Time:= 0;
 ap.Power:= 1;
+
 x:= hwFloat2Float(Me^.X);
 y:= hwFloat2Float(Me^.Y);
+
 if Abs(trunc(x) - Targ.X) + Abs(trunc(y) - Targ.Y) < 40 then
-    begin
-    TestDesertEagle:= BadTurn;
     exit(BadTurn);
-    end;
+
 t:= 2 / sqrt(sqr(Targ.X - x)+sqr(Targ.Y-y));
 Vx:= (Targ.X - x) * t;
 Vy:= (Targ.Y - y) * t;
@@ -650,7 +651,7 @@
     d: Longword;
     fallDmg, valueResult: LongInt;
 begin
-if Me^.Hedgehog^.BotLevel > 3 then exit(BadTurn);
+if Level > 3 then exit(BadTurn);
 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
 Level:= Level; // avoid compiler hint
 ap.ExplR:= 0;
@@ -700,28 +701,28 @@
     x, y, trackFall: LongInt;
     dx, dy: real;
 begin
-    if Me^.Hedgehog^.BotLevel < 3 then trackFall:= afTrackFall
+    if Level < 3 then trackFall:= afTrackFall
     else trackFall:= 0;
-    Level:= Level; // avoid compiler hint
+
     ap.ExplR:= 0;
     ap.Time:= 0;
     ap.Power:= 1;
     x:= hwRound(Me^.X);
     y:= hwRound(Me^.Y);
 
-    a:= 0;
+    a:= cMaxAngle div 2;
     valueResult:= 0;
 
-    while a <= cMaxAngle div 2 do
+    while a >= 0 do
         begin
         dx:= sin(a / cMaxAngle * pi) * 0.5;
         dy:= cos(a / cMaxAngle * pi) * 0.5;
 
-        v1:= RateShove(Me, x - 10, y
-                , 33, 30, 115
+        v1:= RateShove(Me, x - 10, y + 2
+                , 32, 30, 115
                 , -dx, -dy, trackFall);
-        v2:= RateShove(Me, x + 10, y
-                , 33, 30, 115
+        v2:= RateShove(Me, x + 10, y + 2
+                , 32, 30, 115
                 , dx, -dy, trackFall);
         if (v1 > valueResult) or (v2 > valueResult) then
             if (v2 > v1) 
@@ -736,7 +737,7 @@
                 valueResult:= v1
                 end;
 
-        a:= a + 15 + random(cMaxAngle div 16)
+        a:= a - 15 - random(cMaxAngle div 16)
         end;
    
     if valueResult <= 0 then
@@ -749,24 +750,24 @@
 var valueResult, v1, v2, i: LongInt;
     x, y, trackFall: LongInt;
 begin
-    if Me^.Hedgehog^.BotLevel = 1 then trackFall:= afTrackFall
+    if Level = 1 then trackFall:= afTrackFall
     else trackFall:= 0;
-    Level:= Level; // avoid compiler hint
+
     ap.ExplR:= 0;
     ap.Time:= 0;
     ap.Power:= 1;
     x:= hwRound(Me^.X);
-    y:= hwRound(Me^.Y);
+    y:= hwRound(Me^.Y) + 4;
 
     v1:= 0;
     for i:= 0 to 8 do
         begin
-        v1:= v1 + RateShove(Me, x - 10, y - 10 * i
-                , 18, 30, 40
+        v1:= v1 + RateShove(Me, x - 5, y - 10 * i
+                , 19, 30, 40
                 , -0.45, -0.9, trackFall or afSetSkip);
         end;
-    v1:= v1 + RateShove(Me, x - 10, y - 90
-            , 18, 30, 40
+    v1:= v1 + RateShove(Me, x - 5, y - 90
+            , 19, 30, 40
             , -0.45, -0.9, trackFall);
 
 
@@ -774,12 +775,12 @@
     v2:= 0;
     for i:= 0 to 8 do
         begin
-        v2:= v2 + RateShove(Me, x + 10, y - 10 * i
-                , 18, 30, 40
+        v2:= v2 + RateShove(Me, x + 5, y - 10 * i
+                , 19, 30, 40
                 , 0.45, -0.9, trackFall or afSetSkip);
         end;
-    v2:= v2 + RateShove(Me, x + 10, y - 90
-            , 18, 30, 40
+    v2:= v2 + RateShove(Me, x + 5, y - 90
+            , 19, 30, 40
             , 0.45, -0.9, trackFall);
 
     if (v2 > v1) 
@@ -805,9 +806,9 @@
 var valueResult, v1, v2: LongInt;
     x, y, trackFall: LongInt;
 begin
-    if Me^.Hedgehog^.BotLevel = 1 then trackFall:= afTrackFall
+    if Level = 1 then trackFall:= afTrackFall
     else trackFall:= 0;
-    Level:= Level; // avoid compiler hint
+
     ap.ExplR:= 0;
     ap.Time:= 0;
     ap.Power:= 1;
@@ -818,19 +819,19 @@
     {first RateShove checks farthermost of two whip's AmmoShove attacks 
     to encourage distant attacks (damaged hog is excluded from view of second 
     RateShove call)}
-    v1:= RateShove(Me, x - 15, y
+    v1:= RateShove(Me, x - 13, y
             , 30, 30, 25
             , -1, -0.8, trackFall or afSetSkip);
     v1:= v1 +
-        RateShove(Me, x, y
+        RateShove(Me, x - 2, y
             , 30, 30, 25
             , -1, -0.8, trackFall);
     // now try opposite direction
-    v2:= RateShove(Me, x + 15, y
+    v2:= RateShove(Me, x + 13, y
             , 30, 30, 25
             , 1, -0.8, trackFall or afSetSkip);
     v2:= v2 +
-        RateShove(Me, x, y
+        RateShove(Me, x + 2, y
             , 30, 30, 25
             , 1, -0.8, trackFall);
 
@@ -854,6 +855,90 @@
     TestWhip:= valueResult;
 end;
 
+function TestKamikaze(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+const step = 8;
+var valueResult, i, v, tx: LongInt;
+    trackFall: LongInt;
+    t, d, x, y, dx, dy, cx: real;
+begin
+    ap.ExplR:= 0;
+    ap.Time:= 0;
+    ap.Power:= 1;
+
+    if Level = 1 then 
+        trackFall:= afTrackFall
+    else if Level = 2 then
+        trackFall:= 0
+    else
+        exit(BadTurn);
+        
+    valueResult:= 0;
+    v:= 0;
+
+    x:= hwFloat2Float(Me^.X);
+    y:= hwFloat2Float(Me^.Y);
+    d:= sqrt(sqr(Targ.X - x) + sqr(Targ.Y - y));
+    if d < 10 then
+        begin
+        dx:= 0;
+        dy:= 8;
+        ap.Angle:= 2048
+        end
+    else
+        begin
+        t:= step / d;
+        dx:= (Targ.X - x) * t;
+        dy:= (Targ.Y - y) * t;
+
+        ap.Angle:= DxDy2AttackAnglef(dx, -dy)
+        end;
+    
+    if dx >= 0 then cx:= 0.45 else cx:= -0.45;
+
+    for i:= 0 to 512 div step - 2 do
+        begin
+        valueResult:= valueResult + 
+            RateShove(Me, trunc(x), trunc(y)
+                , 30, 30, 25
+                , cx, -0.9, trackFall or afSetSkip);
+                
+        x:= x + dx;
+        y:= y + dy;
+        end;
+    if dx = 0 then
+        begin
+        x:= hwFloat2Float(Me^.X);
+        y:= hwFloat2Float(Me^.Y);
+        tx:= trunc(x);
+        v:= RateShove(Me, tx, trunc(y)
+                , 30, 30, 25
+                , -cx, -0.9, trackFall);
+        for i:= 1 to 512 div step - 2 do
+            begin
+            y:= y + dy;
+            v:= v + 
+                RateShove(Me, tx, trunc(y)
+                    , 30, 30, 25
+                    , -cx, -0.9, trackFall or afSetSkip);
+            end
+        end;
+    if v > valueResult then
+        begin
+        ap.Angle:= -2048;
+        valueResult:= v
+        end;
+        
+    v:= RateShove(Me, trunc(x), trunc(y)
+            , 30, 30, 25
+            , cx, -0.9, trackFall);
+    valueResult:= valueResult + v - KillScore * friendlyfactor div 100 * 1024;
+    
+    if v < 65536 then
+        inc(valueResult, RateExplosion(Me, trunc(x), trunc(y), 30));
+
+    TestKamikaze:= valueResult;
+end;
+
 function TestHammer(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
 var rate: LongInt;
 begin
@@ -983,9 +1068,24 @@
 end;
 
 
-function checkCakeWalk(Gear: PGear): LongInt;
+procedure checkCakeWalk(Me, Gear: PGear; var ap: TAttackParams);
+var i: Longword;
+    v: LongInt;
 begin
-checkCakeWalk:= BadTurn
+while (not TestColl(hwRound(Gear^.X), hwRound(Gear^.Y), 6)) and (Gear^.Y.Round < LAND_HEIGHT) do
+    Gear^.Y:= Gear^.Y + _1;
+
+for i:= 0 to 2040 do
+    begin
+    cakeStep(Gear);
+    v:= RateExplosion(Me, hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg * 2, afTrackFall);
+    if v > ap.Power then 
+        begin
+        ap.ExplX:= hwRound(Gear^.X);
+        ap.ExplY:= hwRound(Gear^.Y);
+        ap.Power:= v
+        end
+    end;
 end;
 
 function TestCake(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
@@ -993,26 +1093,33 @@
     x, y, trackFall: LongInt;
     cake: TGear;
 begin
-    Level:= Level; // avoid compiler hint
+    if (Level > 2) then
+        exit(BadTurn);
     ap.ExplR:= 0;
     ap.Time:= 0;
-    ap.Power:= 1;
+    ap.Power:= BadTurn; // use it as max score value in checkCakeWalk
 
+    FillChar(cake, sizeof(cake), 0);
     cake.Radius:= 7;
+    cake.CollisionMask:= $FF7F;
 
     // check left direction
     cake.Angle:= 3;
     cake.dX.isNegative:= true;
     cake.X:= Me^.X - _3;
     cake.Y:= Me^.Y;
-    v1:= checkCakeWalk(@cake);
+    checkCakeWalk(Me, @cake, ap);
+    v1:= ap.Power;
 
     // now try opposite direction
     cake.Angle:= 1;
     cake.dX.isNegative:= false;
     cake.X:= Me^.X + _3;
     cake.Y:= Me^.Y;
-    v2:= checkCakeWalk(@cake);
+    checkCakeWalk(Me, @cake, ap);
+    v2:= ap.Power;
+
+    ap.Power:= 1;
 
     if (v2 > v1) then
         begin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uAILandMarks.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,71 @@
+unit uAILandMarks;
+
+interface
+const markWasHere = $01;
+
+procedure addMark(X, Y: LongInt; mark: byte);
+function  checkMark(X, Y: LongInt; mark: byte) : boolean;
+procedure clearAllMarks;
+procedure clearMarks(mark: byte);
+
+procedure initModule;
+procedure freeModule;
+
+implementation
+uses uVariables;
+
+const gr = 2;
+
+var marks: array of array of byte;
+    WIDTH, HEIGHT: Longword;
+
+procedure addMark(X, Y: LongInt; mark: byte);
+begin
+    if((X and LAND_WIDTH_MASK) = 0) and ((Y and LAND_HEIGHT_MASK) = 0) then
+        begin
+        X:= X shr gr;
+        Y:= Y shr gr;
+        marks[Y, X]:= marks[Y, X] or mark
+        end
+end;
+
+function  checkMark(X, Y: LongInt; mark: byte) : boolean;
+begin
+    checkMark:= ((X and LAND_WIDTH_MASK) = 0) 
+        and ((Y and LAND_HEIGHT_MASK) = 0) 
+        and ((marks[Y shr gr, X shr gr] and mark) <> 0)
+end;
+
+procedure clearAllMarks;
+var 
+    Y, X: Longword;
+begin
+    for Y:= 0 to Pred(HEIGHT) do
+        for X:= 0 to Pred(WIDTH) do
+            marks[Y, X]:= 0
+end;
+
+procedure clearMarks(mark: byte);
+var 
+    Y, X: Longword;
+begin
+    for Y:= 0 to Pred(HEIGHT) do
+        for X:= 0 to Pred(WIDTH) do
+            marks[Y, X]:= marks[Y, X] and (not mark)
+end;
+
+
+procedure initModule;
+begin
+    WIDTH:= LAND_WIDTH shr gr;
+    HEIGHT:= LAND_HEIGHT shr gr;
+    
+    SetLength(marks, HEIGHT, WIDTH);
+end;
+
+procedure freeModule;
+begin
+    SetLength(marks, 0, 0);
+end;
+
+end.
--- a/hedgewars/uAIMisc.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAIMisc.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -86,12 +86,13 @@
         ar: array[0..Pred(MAXBONUS div 8)] of TBonus;  // don't use too many
         end;
 
+const KillScore = 200;
+var friendlyfactor: LongInt = 300;
+       
 implementation
 uses uCollisions, uVariables, uUtils, uDebug, uLandTexture;
 
-const KillScore = 200;
-
-var friendlyfactor: LongInt = 300;
+var 
     KnownExplosion: record
         X, Y, Radius: LongInt
         end = (X: 0; Y: 0; Radius: 0);
@@ -170,7 +171,7 @@
     begin
         case Gear^.Kind of
             gtCase:
-            AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 33, 25);
+                AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y) + 3, 37, 25);
             gtFlame:
                 if (Gear^.State and gsttmpFlag) <> 0 then
                     AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 20, -50);
@@ -371,13 +372,13 @@
         x:= x + dX;
         y:= y + dY;
         dY:= dY + cGravityf;
-(*
-        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then 
+
+{        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then 
             begin
             LandPixels[trunc(y), trunc(x)]:= v;
             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
-            end;
-*)
+            end;}
+
 
         // consider adding dX/dY calc here for fall damage
         if TestCollExcludingObjects(trunc(x), trunc(y), cHHRadius) then
@@ -439,21 +440,21 @@
                 end;
             if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
                 if Score > 0 then
-                    inc(rate, KillScore + Score div 10)   // Add a bit of a bonus for bigger hog drownings
+                    inc(rate, (KillScore + Score div 10) * 1024)   // Add a bit of a bonus for bigger hog drownings
                 else
-                    dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
+                    dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
             else if (dmg+fallDmg) >= abs(Score) then
                 if Score > 0 then
-                    inc(rate, KillScore)
+                    inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
                 else
-                    dec(rate, KillScore * friendlyfactor div 100)
+                    dec(rate, KillScore * friendlyfactor div 100 * 1024)
             else
                 if Score > 0 then
-                    inc(rate, dmg+fallDmg)
-                else dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
+                    inc(rate, (dmg + fallDmg) * 1024)
+                else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
             end;
         end;
-RateExplosion:= rate * 1024;
+RateExplosion:= rate;
 end;
 
 function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
@@ -624,6 +625,12 @@
 end;
 
 repeat
+        {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then 
+            begin
+            LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color;
+            UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
+            end;}
+            
     if not (hwRound(Gear^.Y) + cHHRadius < cWaterLine) then
         exit(false);
     if (Gear^.State and gstMoving) <> 0 then
@@ -645,7 +652,7 @@
         Gear^.Y:= Gear^.Y + Gear^.dY;
         if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then
             begin
-            Gear^.State:= Gear^.State and not (gstMoving or gstHHJumping);
+            Gear^.State:= Gear^.State and (not (gstMoving or gstHHJumping));
             Gear^.dY:= _0;
             case JumpType of
                 jmpHJump:
@@ -673,6 +680,7 @@
 var pX, pY, tY: LongInt;
 begin
 HHGo:= false;
+Gear^.CollisionMask:= $FF7F;
 AltGear^:= Gear^;
 
 GoInfo.Ticks:= 0;
@@ -680,6 +688,12 @@
 GoInfo.JumpType:= jmpNone;
 tY:= hwRound(Gear^.Y);
 repeat
+        {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then 
+            begin
+            LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
+            UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
+            end;}
+
     pX:= hwRound(Gear^.X);
     pY:= hwRound(Gear^.Y);
     if pY + cHHRadius >= cWaterLine then
@@ -696,7 +710,7 @@
         Gear^.dY:= Gear^.dY + cGravity;
         if Gear^.dY > _0_4 then
             begin
-            Goinfo.FallPix:= 0;
+            GoInfo.FallPix:= 0;
             // try ljump instead of fall with damage
             HHJump(AltGear, jmpLJump, GoInfo); 
             if AltGear^.Hedgehog^.BotLevel < 4 then
@@ -709,7 +723,7 @@
         if TestCollisionYwithGear(Gear, 1) <> 0 then
             begin
             inc(GoInfo.Ticks, 410);
-            Gear^.State:= Gear^.State and not (gstMoving or gstHHJumping);
+            Gear^.State:= Gear^.State and (not (gstMoving or gstHHJumping));
             Gear^.dY:= _0;
             // try ljump instead of fall
             HHJump(AltGear, jmpLJump, GoInfo);
--- a/hedgewars/uAmmos.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAmmos.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -374,7 +374,7 @@
     with CurWeapon^ do
         begin
         s:= trammo[Ammoz[AmmoType].NameId];
-        if (Count <> AMMO_INFINITE) and not (Hedgehog.Team^.ExtDriven or (Hedgehog.BotLevel > 0)) then
+        if (Count <> AMMO_INFINITE) and (not (Hedgehog.Team^.ExtDriven or (Hedgehog.BotLevel > 0))) then
             s:= s + ' (' + IntToStr(Count) + ')';
         if (Propz and ammoprop_Timerable) <> 0 then
             s:= s + ', ' + IntToStr(Timer div 1000) + ' ' + trammo[sidSeconds];
@@ -386,7 +386,7 @@
             end
         else
             begin
-            if Gear <> nil then Gear^.State:= Gear^.State and not gstHHChooseTarget;
+            if Gear <> nil then Gear^.State:= Gear^.State and (not gstHHChooseTarget);
             isCursorVisible:= false
             end;
         end
--- a/hedgewars/uCollisions.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uCollisions.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -82,7 +82,7 @@
     X:= hwRound(Gear^.X);
     Y:= hwRound(Gear^.Y);
     Radius:= Gear^.Radius;
-    ChangeRoundInLand(X, Y, Radius - 1, true, Gear = CurrentHedgehog^.Gear);
+    ChangeRoundInLand(X, Y, Radius - 1, true, (Gear = CurrentHedgehog^.Gear) or (Gear^.Kind = gtCase));
     cGear:= Gear
     end;
 Gear^.CollisionIndex:= Count;
@@ -103,7 +103,7 @@
 if Gear^.CollisionIndex >= 0 then
     begin
     with cinfos[Gear^.CollisionIndex] do
-        ChangeRoundInLand(X, Y, Radius - 1, false, Gear = CurrentHedgehog^.Gear);
+        ChangeRoundInLand(X, Y, Radius - 1, false, (Gear = CurrentHedgehog^.Gear) or (Gear^.Kind = gtCase));
     cinfos[Gear^.CollisionIndex]:= cinfos[Pred(Count)];
     cinfos[Gear^.CollisionIndex].cGear^.CollisionIndex:= Gear^.CollisionIndex;
     Gear^.CollisionIndex:= -1;
@@ -138,7 +138,7 @@
 var x, y, i: LongInt;
 begin
 // Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = $FF7F) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = $FF7F) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
     ((hwRound(Gear^.Hedgehog^.Gear^.X) + Gear^.Hedgehog^.Gear^.Radius + 4 < hwRound(Gear^.X) - Gear^.Radius) or
      (hwRound(Gear^.Hedgehog^.Gear^.X) - Gear^.Hedgehog^.Gear^.Radius - 4 > hwRound(Gear^.X) + Gear^.Radius)) then
     Gear^.CollisionMask:= $FFFF;
@@ -168,7 +168,7 @@
 var x, y, i: LongInt;
 begin
 // Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = $FF7F) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = $FF7F) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
     ((hwRound(Gear^.Hedgehog^.Gear^.Y) + Gear^.Hedgehog^.Gear^.Radius + 4 < hwRound(Gear^.Y) - Gear^.Radius) or
      (hwRound(Gear^.Hedgehog^.Gear^.Y) - Gear^.Hedgehog^.Gear^.Radius - 4 > hwRound(Gear^.Y) + Gear^.Radius)) then
     Gear^.CollisionMask:= $FFFF;
--- a/hedgewars/uCommands.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uCommands.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -93,9 +93,12 @@
     begin
     if t^.Name = CmdStr then
         begin
-        if t^.Rand then CheckSum:= CheckSum xor LongWord(SDLNet_Read32(@CmdStr)) xor LongWord(s[0]) xor GameTicks;
         if TrustedSource or t^.Trusted then
+            begin
+            if t^.Rand and (not CheckNoTeamOrHH) then 
+                CheckSum:= CheckSum xor LongWord(SDLNet_Read32(@CmdStr)) xor LongWord(s[0]) xor GameTicks;
             t^.Handler(s);
+            end;
         exit
         end
     else
--- a/hedgewars/uConsts.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uConsts.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -28,6 +28,7 @@
 const
     sfMax = 1000;
     cDefaultParamNum = 17;
+    cVideorecParamNum = cDefaultParamNum + 7;
 
     // message constants
     errmsgCreateSurface   = 'Error creating SDL surface';
@@ -146,6 +147,7 @@
     cBarrelHealth = 60;
     cShotgunRadius = 22;
     cBlowTorchC    = 6;
+    cakeDmg =   75;
 
     cKeyMaxIndex = 1023;
     cKbdMaxIndex = 65536;//need more room for the modifier keys
@@ -244,6 +246,7 @@
 
     gmRemoveFromList = $00004000;
     gmAddToList      = $00008000;
+    gmDelete         = $00010000;
     gmAllStoppable = gmLeft or gmRight or gmUp or gmDown or gmAttack or gmPrecise;
 
     cMaxSlotIndex       = 9;
@@ -266,6 +269,7 @@
     ammoprop_NeedUpDown   = $00008000;//Used by TouchInterface to show or hide up/down widgets 
     ammoprop_OscAim       = $00010000;
     ammoprop_NoMoveAfter  = $00020000;
+    ammoprop_Track        = $00040000;
     ammoprop_NoRoundEnd   = $10000000;
 
     AMMO_INFINITE = 100;
--- a/hedgewars/uFloat.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uFloat.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -119,6 +119,7 @@
           _0_005: hwFloat = (isNegative: false; QWordValue:    21474836);
           _0_008: hwFloat = (isNegative: false; QWordValue:    34359738);
            _0_01: hwFloat = (isNegative: false; QWordValue:    42949673);
+         _0_0128: hwFloat = (isNegative: false; QWordValue:    54975581);
            _0_02: hwFloat = (isNegative: false; QWordValue:    85899345);
            _0_03: hwFloat = (isNegative: false; QWordValue:   128849018);
            _0_07: hwFloat = (isNegative: false; QWordValue:   300647710);
@@ -149,14 +150,18 @@
               _0: hwFloat = (isNegative: false; QWordValue:           0);
               _1: hwFloat = (isNegative: false; QWordValue:  4294967296);
             _1_5: hwFloat = (isNegative: false; QWordValue:  4294967296 * 3 div 2);
+            _1_6: hwFloat = (isNegative: false; QWordValue:  4294967296 * 8 div 5);
             _1_9: hwFloat = (isNegative: false; QWordValue:  8160437862);
               _2: hwFloat = (isNegative: false; QWordValue:  4294967296 * 2);
+            _2_4: hwFloat = (isNegative: false; QWordValue:  4294967296 * 12 div 5);
               _3: hwFloat = (isNegative: false; QWordValue:  4294967296 * 3);
              _PI: hwFloat = (isNegative: false; QWordValue: 13493037704);
               _4: hwFloat = (isNegative: false; QWordValue:  4294967296 * 4);
             _4_5: hwFloat = (isNegative: false; QWordValue:  4294967296 * 9 div 2);
               _5: hwFloat = (isNegative: false; QWordValue:  4294967296 * 5);
               _6: hwFloat = (isNegative: false; QWordValue:  4294967296 * 6);
+            _6_4: hwFloat = (isNegative: false; QWordValue:  3435973837 * 8);
+              _7: hwFloat = (isNegative: false; QWordValue:  4294967296 * 7);
              _10: hwFloat = (isNegative: false; QWordValue:  4294967296 * 10);
              _12: hwFloat = (isNegative: false; QWordValue:  4294967296 * 12);
              _16: hwFloat = (isNegative: false; QWordValue:  4294967296 * 16);
@@ -165,6 +170,8 @@
              _25: hwFloat = (isNegative: false; QWordValue:  4294967296 * 25);
              _30: hwFloat = (isNegative: false; QWordValue:  4294967296 * 30);
              _40: hwFloat = (isNegative: false; QWordValue:  4294967296 * 40);
+             _41: hwFloat = (isNegative: false; QWordValue:  4294967296 * 41);
+             _49: hwFloat = (isNegative: false; QWordValue:  4294967296 * 49);
              _50: hwFloat = (isNegative: false; QWordValue:  4294967296 * 50);
              _70: hwFloat = (isNegative: false; QWordValue:  4294967296 * 70);
              _90: hwFloat = (isNegative: false; QWordValue:  4294967296 * 90);
@@ -196,20 +203,21 @@
 
 {$IFDEF FPC}
 
-function int2hwFloat (const i: LongInt) : hwFloat;
+function int2hwFloat (const i: LongInt) : hwFloat; inline;
 begin
 int2hwFloat.isNegative:= i < 0;
 int2hwFloat.Round:= abs(i);
 int2hwFloat.Frac:= 0
 end;
 
-function hwFloat2Float (const i: hwFloat) : extended;
+function hwFloat2Float (const i: hwFloat) : extended; inline;
 begin
-hwFloat2Float:= i.QWordValue / $100000000;
+hwFloat2Float:= i.Frac / $100000000 + i.Round;
 if i.isNegative then
     hwFloat2Float:= -hwFloat2Float;
 end;
 
+{$IFNDEF WEB}
 operator = (const z1, z2: hwFloat) z : boolean; inline;
 begin
     z:= (z1.isNegative = z2.isNegative) and (z1.QWordValue = z2.QWordValue);
@@ -222,7 +230,7 @@
 end;
 {$ENDIF}
 
-operator + (const z1, z2: hwFloat) z : hwFloat;
+operator + (const z1, z2: hwFloat) z : hwFloat; inline;
 begin
 if z1.isNegative = z2.isNegative then
     begin
@@ -242,7 +250,7 @@
         end
 end;
 
-operator - (const z1, z2: hwFloat) z : hwFloat;
+operator - (const z1, z2: hwFloat) z : hwFloat; inline;
 begin
 if z1.isNegative = z2.isNegative then
     if z1.QWordValue > z2.QWordValue then
@@ -262,27 +270,150 @@
     end
 end;
 
-operator - (const z1: hwFloat) z : hwFloat;
+function isZero(const z: hwFloat): boolean; inline; 
+begin
+isZero := z.QWordValue = 0;
+end;
+
+operator < (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+    b:= z1.isNegative
+else
+    if z1.QWordValue = z2.QWordValue then
+        b:= false
+    else
+        b:= not((z1.QWordValue = z2.QWordValue) or ((z2.QWordValue < z1.QWordValue) <> z1.isNegative))
+end;
+
+operator > (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+    b:= z2.isNegative
+else
+    if z1.QWordValue = z2.QWordValue then
+        b:= false
+    else
+        b:= (z1.QWordValue > z2.QWordValue) <> z2.isNegative
+end;
+{$ENDIF}
+{$IFDEF WEB}
+(*
+    Mostly to be kind to JS as of 2012-08-27 where there is no int64/uint64.  This may change though.
+*)
+operator = (const z1, z2: hwFloat) z : boolean; inline;
+begin
+    z:= (z1.isNegative = z2.isNegative) and (z1.Frac = z2.Frac) and (z1.Round = z2.Round);
+end;
+
+operator <> (const z1, z2: hwFloat) z : boolean; inline;
+begin
+    z:= (z1.isNegative <> z2.isNegative) or (z1.Frac <> z2.Frac) or (z1.Round <> z2.Round);
+end;
+
+operator + (const z1, z2: hwFloat) z : hwFloat; inline;
+begin
+if z1.isNegative = z2.isNegative then
+    begin
+    z:= z1;
+    z.Frac:= z.Frac + z2.Frac;
+    z.Round:= z.Round + z2.Round;
+    if z.Frac<z1.Frac then inc(z.Round)
+    end
+else
+    if (z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac)) then
+        begin
+        z.isNegative:= z1.isNegative;
+        z.Round:= z1.Round - z2.Round;
+        z.Frac:= z1.Frac - z2.Frac;
+        if z2.Frac > z1.Frac then dec(z.Round)
+        end
+    else
+        begin
+        z.isNegative:= z2.isNegative;
+        z.Round:= z2.Round - z1.Round;
+        z.Frac:= z2.Frac-z1.Frac;
+        if z2.Frac < z1.Frac then dec(z.Round)
+        end
+end;
+
+operator - (const z1, z2: hwFloat) z : hwFloat; inline;
+begin
+if z1.isNegative = z2.isNegative then
+    if (z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac)) then
+        begin
+        z.isNegative:= z1.isNegative;
+        z.Round:= z1.Round - z2.Round;
+        z.Frac:= z1.Frac-z2.Frac;
+        if z2.Frac > z1.Frac then dec(z.Round)
+        end
+    else
+        begin
+        z.isNegative:= not z2.isNegative;
+        z.Round:= z2.Round - z1.Round;
+        z.Frac:= z2.Frac-z1.Frac;
+        if z2.Frac < z1.Frac then dec(z.Round)
+        end
+else
+    begin
+    z:= z1;
+    z.Frac:= z.Frac + z2.Frac;
+    z.Round:= z.Round + z2.Round;
+    if z.Frac<z1.Frac then inc(z.Round)
+    end
+end;
+
+operator < (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+    b:= z1.isNegative
+else
+(*  Not so sure this specialcase is a win w/ Round/Frac. have to do more tests anyway.
+    if (z1.Round = z2.Round and (z1.Frac = z2.Frac)) then
+        b:= false
+    else *)
+        b:= ((z1.Round < z2.Round) or ((z1.Round = z2.Round) and (z1.Frac < z2.Frac))) <> z1.isNegative
+end;
+
+operator > (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+    b:= z2.isNegative
+else
+(*
+    if z1.QWordValue = z2.QWordValue then
+        b:= false
+    else*)
+        b:= ((z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac))) <> z1.isNegative
+end;
+
+function isZero(const z: hwFloat): boolean; inline; 
+begin
+isZero := z.Round = 0 and z.Frac = 0;
+end;
+{$ENDIF}
+
+operator - (const z1: hwFloat) z : hwFloat; inline;
 begin
 z:= z1;
 z.isNegative:= not z.isNegative
 end;
 
 
-operator * (const z1, z2: hwFloat) z : hwFloat;
+operator * (const z1, z2: hwFloat) z : hwFloat; inline;
 begin
 z.isNegative:= z1.isNegative xor z2.isNegative;
 z.QWordValue:= QWord(z1.Round) * z2.Frac + QWord(z1.Frac) * z2.Round + ((QWord(z1.Frac) * z2.Frac) shr 32);
 z.Round:= z.Round + QWord(z1.Round) * z2.Round;
 end;
 
-operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat;
+operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
 begin
 z.isNegative:= z1.isNegative xor (z2 < 0);
 z.QWordValue:= z1.QWordValue * abs(z2)
 end;
 
-operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat;
+operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline;
 var t: hwFloat;
 begin
 z.isNegative:= z1.isNegative xor z2.isNegative;
@@ -304,34 +435,12 @@
     end
 end;
 
-operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat;
+operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
 begin
 z.isNegative:= z1.isNegative xor (z2 < 0);
 z.QWordValue:= z1.QWordValue div abs(z2)
 end;
 
-operator < (const z1, z2: hwFloat) b : boolean;
-begin
-if z1.isNegative xor z2.isNegative then
-    b:= z1.isNegative
-else
-    if z1.QWordValue = z2.QWordValue then
-        b:= false
-    else
-        b:= (z1.QWordValue < z2.QWordValue) xor z1.isNegative
-end;
-
-operator > (const z1, z2: hwFloat) b : boolean;
-begin
-if z1.isNegative xor z2.isNegative then
-    b:= z2.isNegative
-else
-    if z1.QWordValue = z2.QWordValue then
-        b:= false
-    else
-        b:= (z1.QWordValue > z2.QWordValue) xor z2.isNegative
-end;
-
 function cstr(const z: hwFloat): shortstring;
 var tmpstr: shortstring;
 begin
@@ -360,7 +469,7 @@
 hwAbs.isNegative:= false
 end;
 
-function hwSqr(const t: hwFloat): hwFloat;
+function hwSqr(const t: hwFloat): hwFloat; inline;
 begin
 hwSqr.isNegative:= false;
 hwSqr.QWordValue:= ((QWord(t.Round) * t.Round) shl 32) + QWord(t.Round) * t.Frac * 2 + ((QWord(t.Frac) * t.Frac) shr 32);
@@ -369,7 +478,7 @@
 function hwPow(const t: hwFloat;p: LongWord): hwFloat;
 begin
 hwPow:= t;
-if p mod 2 = 0 then hwPow.isNegative:= t.isNegative;
+if p mod 2 = 0 then hwPow.isNegative:= false;
 
 while p > 0 do
     begin
@@ -461,11 +570,6 @@
 else
     AngleCos.QWordValue:= SinTable[Angle - 1024]
 end;
-
-function isZero(const z: hwFloat): boolean; inline; 
-begin
-isZero := z.QWordValue = 0;
-end;
 {$ENDIF}
 
 end.
--- a/hedgewars/uGame.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGame.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -39,17 +39,27 @@
     isInLag:= false;
     SendKeepAliveMessage(Lag)
     end;
-if Lag > 100 then
-    Lag:= 100
-else if (GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) then
-    Lag:= 2500;
+if GameType <> gmtRecord then
+    begin
+    if Lag > 100 then
+        Lag:= 100
+    else if (GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) then
+        Lag:= 2500;
 
-if (GameType = gmtDemo) then 
-    if isSpeed then
-        Lag:= Lag * 10
-    else
-        if cOnlyStats then
-            Lag:= High(LongInt);
+    if (GameType = gmtDemo) then 
+        if isSpeed then
+        begin
+            i:= RealTicks-SpeedStart;
+            if i < 2000 then Lag:= Lag*5
+            else if i < 4000 then Lag:= Lag*10
+            else if i < 6000 then Lag:= Lag*20
+            else if i < 8000 then Lag:= Lag*40
+            else Lag:= Lag*80;
+        end
+        else
+            if cOnlyStats then
+                Lag:= High(LongInt);
+    end;
 PlayNextVoice;
 i:= 1;
 while (GameState <> gsExit) and (i <= Lag) do
@@ -71,7 +81,7 @@
                         AddVisualGear(0, 0, vgtTeamHealthSorter);
                         break;
                         end;
-                gmtDemo: begin
+                gmtDemo, gmtRecord: begin
                         GameState:= gsExit;
                         exit
                         end;
--- a/hedgewars/uGears.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGears.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -59,7 +59,7 @@
 uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics,
     uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uMobile, uVariables,
     uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture,
-    uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlers;
+    uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlers, uGearsHandlersRope;
 
 var skipFlag: boolean;
 
@@ -77,6 +77,7 @@
     stAfterDelay, stChWin, stWater, stChWin2, stHealth,
     stSpawn, stNTurn);
     upd: Longword;
+    snowLeft,snowRight: LongInt;
     //SDMusic: shortstring;
 
 // For better maintainability the step handlers of gears are stored in
@@ -206,23 +207,28 @@
     curHandledGear:= t;
     t:= curHandledGear^.NextGear;
 
-    if curHandledGear^.Message and gmRemoveFromList <> 0 then 
-        begin
-        RemoveGearFromList(curHandledGear);
-        // since I can't think of any good reason this would ever be separate from a remove from list, going to keep it inside this block
-        if curHandledGear^.Message and gmAddToList <> 0 then InsertGearToList(curHandledGear);
-        curHandledGear^.Message:= curHandledGear^.Message and not (gmRemoveFromList or gmAddToList)
-        end;
-    if curHandledGear^.Active then
+    if curHandledGear^.Message and gmDelete <> 0 then
+        DeleteGear(curHandledGear)
+    else
         begin
-        if curHandledGear^.RenderTimer and (curHandledGear^.Timer > 500) and ((curHandledGear^.Timer mod 1000) = 0) then
+        if curHandledGear^.Message and gmRemoveFromList <> 0 then 
+            begin
+            RemoveGearFromList(curHandledGear);
+            // since I can't think of any good reason this would ever be separate from a remove from list, going to keep it inside this block
+            if curHandledGear^.Message and gmAddToList <> 0 then InsertGearToList(curHandledGear);
+            curHandledGear^.Message:= curHandledGear^.Message and (not (gmRemoveFromList or gmAddToList))
+            end;
+        if curHandledGear^.Active then
             begin
-            FreeTexture(curHandledGear^.Tex);
-            curHandledGear^.Tex:= RenderStringTex(inttostr(curHandledGear^.Timer div 1000), cWhiteColor, fntSmall);
-            end;
-        curHandledGear^.doStep(curHandledGear);
-        // might be useful later
-        //ScriptCall('onGearStep', Gear^.uid);
+            if curHandledGear^.RenderTimer and (curHandledGear^.Timer > 500) and ((curHandledGear^.Timer mod 1000) = 0) then
+                begin
+                FreeTexture(curHandledGear^.Tex);
+                curHandledGear^.Tex:= RenderStringTex(inttostr(curHandledGear^.Timer div 1000), cWhiteColor, fntSmall);
+                end;
+            curHandledGear^.doStep(curHandledGear);
+            // might be useful later
+            //ScriptCall('onGearStep', Gear^.uid);
+            end
         end
     end;
 curHandledGear:= nil;
@@ -634,18 +640,21 @@
 
 if (GameFlags and gfArtillery) <> 0 then
     cArtillery:= true;
-
-for i:= 0 to GetRandom(10)+30 do
-    begin                                                                                                                                       rx:= GetRandom(rightX-leftX)+leftX;
+for i:= GetRandom(10)+30 downto 0 do
+    begin
+    rx:= GetRandom(rightX-leftX)+leftX;
     ry:= GetRandom(LAND_HEIGHT-topY)+topY;
     rdx:= _90-(GetRandomf*_360);
     rdy:= _90-(GetRandomf*_360);
     AddGear(rx, ry, gtGenericFaller, gstInvisible, rdx, rdy, $FFFFFFFF);
     end;
 
+snowRight:= max(LAND_WIDTH,4096)+512;
+snowLeft:= -(snowRight-LAND_WIDTH);
+
 if not hasBorder and ((Theme = 'Snow') or (Theme = 'Christmas')) then
-    for i:= 0 to Pred(vobCount*2) do
-        AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(LAND_HEIGHT div 2), gtFlake, 0, _0, _0, 0);
+    for i:= vobCount * max(LAND_WIDTH,4096) div 2048 downto 1 do
+        AddGear(GetRandom(snowRight-snowLeft)+snowLeft, LAND_HEIGHT-1300+GetRandom(750), gtFlake, 0, _0, _0, 0);
 end;
 
 
--- a/hedgewars/uGearsHandlers.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsHandlers.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -52,8 +52,8 @@
     dA := hwSign(Gear^.dX);
     xx := dirs[Gear^.Angle].x;
     yy := dirs[Gear^.Angle].y;
-    xxn := dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].x;
-    yyn := dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].y;
+    xxn := dirs[(Gear^.Angle + dA) and 3].x;
+    yyn := dirs[(Gear^.Angle + dA) and 3].y;
 
     if (xx = 0) then
         if TestCollisionYwithGear(Gear, yy) <> 0 then
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uGearsHandlersRope.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,589 @@
+(*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *)
+
+{$INCLUDE "options.inc"}
+unit uGearsHandlersRope;
+interface
+
+uses uTypes;
+
+procedure doStepRope(Gear: PGear);
+
+implementation
+uses uConsts, uFloat, uCollisions, uVariables, uGearsList, uSound, uGearsUtils,
+    uAmmos, uDebug, uUtils, uGearsHedgehog, uGearsRender;
+
+procedure doStepRopeAfterAttack(Gear: PGear);
+var 
+    HHGear: PGear;
+begin
+    HHGear := Gear^.Hedgehog^.Gear;
+    if ((HHGear^.State and gstHHDriven) = 0)
+    or (CheckGearDrowning(HHGear))
+    or (TestCollisionYwithGear(HHGear, 1) <> 0) then
+        begin
+        DeleteGear(Gear);
+        isCursorVisible := false;
+        ApplyAmmoChanges(HHGear^.Hedgehog^);
+        exit
+        end;
+
+    HedgehogChAngle(HHGear);
+
+    if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+        SetLittle(HHGear^.dX);
+
+    if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
+        HHGear^.dY := _0;
+    HHGear^.X := HHGear^.X + HHGear^.dX;
+    HHGear^.Y := HHGear^.Y + HHGear^.dY;
+    HHGear^.dY := HHGear^.dY + cGravity;
+    
+    if (GameFlags and gfMoreWind) <> 0 then
+        HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;
+
+    if (Gear^.Message and gmAttack) <> 0 then
+        begin
+        Gear^.X := HHGear^.X;
+        Gear^.Y := HHGear^.Y;
+
+        ApplyAngleBounds(Gear^.Hedgehog^, amRope);
+
+        Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX);
+        Gear^.dY := -AngleCos(HHGear^.Angle);
+        Gear^.Friction := _4_5 * cRopePercent;
+        Gear^.Elasticity := _0;
+        Gear^.State := Gear^.State and (not gsttmpflag);
+        Gear^.doStep := @doStepRope;
+        end
+end;
+
+procedure unstickHog(Gear, HHGear: PGear);
+var i: LongInt;
+    stuck: Boolean;
+begin
+    if (TestCollisionYwithGear(HHGear, 1) <> 0) and (TestCollisionYwithGear(HHGear, -1) = 0) then
+        begin
+        i:= 1;
+        repeat
+            begin
+            inc(i);
+            stuck:= TestCollisionYwithGear(HHGear, 1) <> 0;
+            if stuck then HHGear^.Y:= HHGear^.Y-_1
+            end
+        until (i = 8) or (not stuck);
+        HHGear^.Y:= HHGear^.Y+_1;
+        // experiment in simulating something the shoppa players apparently expect
+        if Gear^.Message and gmDown <> 0 then
+            begin
+            //HHGear^.dY:= HHGear^.dY / 16;
+            //HHGear^.dY.QWordValue:= 0;
+            HHGear^.dY:= -_0_1;
+            HHGear^.dX:= HHGear^.dX * _1_5;
+            end;
+        if Gear^.Message and gmRight <> 0 then
+            HHGear^.dX.isNegative:= false
+        else if Gear^.Message and gmLeft <> 0 then
+            HHGear^.dX.isNegative:= true
+        end
+    else if (TestCollisionYwithGear(HHGear, -1) <> 0) and (TestCollisionYwithGear(HHGear, 1) = 0) then
+        begin
+        i:= 1;
+        repeat
+            begin
+            inc(i);
+            stuck:= TestCollisionYwithGear(HHGear, -1) <> 0;
+            if stuck then HHGear^.Y:= HHGear^.Y+_1
+            end
+        until (i = 8) or (not stuck);
+        HHGear^.Y:= HHGear^.Y-_1;
+        if Gear^.Message and gmDown <> 0 then
+            begin
+            //HHGear^.dY:= HHGear^.dY / 16;
+            //HHGear^.dY.QWordValue:= 0;
+            HHGear^.dY:= _0_1;
+            HHGear^.dX:= HHGear^.dX * _1_5;
+            end;
+        if Gear^.Message and gmRight <> 0 then
+            HHGear^.dX.isNegative:= true
+        else if Gear^.Message and gmLeft <> 0 then
+            HHGear^.dX.isNegative:= false
+        end;
+    if TestCollisionXwithGear(HHGear, 1) and (not TestCollisionXwithGear(HHGear, -1)) then
+        begin
+        i:= 1;
+        repeat
+            begin
+            inc(i);
+            stuck:= TestCollisionXwithGear(HHGear, 1);
+            if stuck then HHGear^.X:= HHGear^.X-_1
+            end
+        until (i = 8) or (not stuck);
+        HHGear^.X:= HHGear^.X+_1;
+        if Gear^.Message and gmDown <> 0 then
+            begin
+            //HHGear^.dX:= HHGear^.dX / 16;
+            //HHGear^.dX.QWordValue:= 0;
+            HHGear^.dX:= -_0_1;
+            HHGear^.dY:= HHGear^.dY * _1_5;
+            end;
+        if Gear^.Message and gmRight <> 0 then
+            HHGear^.dY.isNegative:= true
+        else if Gear^.Message and gmLeft <> 0 then
+            HHGear^.dY.isNegative:= false
+        end
+    else if TestCollisionXwithGear(HHGear, -1) and (not TestCollisionXwithGear(HHGear, 1)) then
+        begin
+        i:= 1;
+        repeat
+            begin
+            inc(i);
+            stuck:= TestCollisionXwithGear(HHGear, -1);
+            if stuck then HHGear^.X:= HHGear^.X+_1
+            end
+        until (i = 8) or (not stuck);
+        HHGear^.X:= HHGear^.X-_1;
+        if Gear^.Message and gmDown <> 0 then
+            begin
+            //HHGear^.dX:= HHGear^.dX / 16;
+            //HHGear^.dX.QWordValue:= 0;
+            HHGear^.dX:= _0_1;
+            HHGear^.dY:= HHGear^.dY * _1_5;
+            end;
+        if Gear^.Message and gmRight <> 0 then
+            HHGear^.dY.isNegative:= false
+        else if Gear^.Message and gmLeft <> 0 then
+            HHGear^.dY.isNegative:= true
+        end
+end;
+
+procedure RopeDeleteMe(Gear, HHGear: PGear);
+begin
+    PlaySound(sndRopeRelease);
+    HHGear^.dX.QWordValue:= HHGear^.dX.QWordValue div Gear^.stepFreq;
+    HHGear^.dY.QWordValue:= HHGear^.dY.QWordValue div Gear^.stepFreq;
+    with HHGear^ do
+        begin
+        Message := Message and (not gmAttack);
+        State := (State or gstMoving) and (not gstWinner);
+        end;
+    unstickHog(Gear, HHGear);
+    DeleteGear(Gear)
+end;
+
+procedure RopeWaitCollision(Gear, HHGear: PGear);
+begin
+    PlaySound(sndRopeRelease);
+    with HHGear^ do
+        begin
+        Message := Message and (not gmAttack);
+        State := State or gstMoving;
+        end;
+    unstickHog(Gear, HHGear);
+    RopePoints.Count := 0;
+    Gear^.Elasticity := _0;
+    Gear^.doStep := @doStepRopeAfterAttack;
+    HHGear^.dX.QWordValue:= HHGear^.dX.QWordValue div Gear^.stepFreq;
+    HHGear^.dY.QWordValue:= HHGear^.dY.QWordValue div Gear^.stepFreq;
+    Gear^.stepFreq := 1
+end;
+
+procedure doStepRopeWork(Gear: PGear);
+var 
+    HHGear: PGear;
+    len, tx, ty, nx, ny, ropeDx, ropeDy, mdX, mdY, t: hwFloat;
+    lx, ly, cd, i: LongInt;
+    haveCollision,
+    haveDivided: boolean;
+
+begin
+    if GameTicks mod 8 <> 0 then exit;
+
+    HHGear := Gear^.Hedgehog^.Gear;
+    haveCollision:= false;
+    if (Gear^.Message and gmLeft  <> 0) and (not TestCollisionXwithGear(HHGear, -1)) then
+        HHGear^.dX := HHGear^.dX - _0_0128
+    else haveCollision:= true;
+
+    if (Gear^.Message and gmRight <> 0) and (not TestCollisionXwithGear(HHGear,  1)) then
+        HHGear^.dX := HHGear^.dX + _0_0128
+    else haveCollision:= true;
+
+
+    if ((HHGear^.State and gstHHDriven) = 0)
+       or (CheckGearDrowning(HHGear)) or (Gear^.PortalCounter <> 0) then
+        begin
+        RopeDeleteMe(Gear, HHGear);
+        exit
+        end;
+
+    // vector between hedgehog and rope attaching point
+    ropeDx := HHGear^.X - Gear^.X;
+    ropeDy := HHGear^.Y - Gear^.Y;
+
+    if TestCollisionYwithGear(HHGear, 1) = 0 then
+        begin
+
+        // depending on the rope vector we know which X-side to check for collision
+        // in order to find out if the hog can still be moved by gravity
+        if ropeDx.isNegative = RopeDy.IsNegative then
+            cd:= -1
+        else
+            cd:= 1;
+
+        // apply gravity if there is no obstacle
+        if not TestCollisionXwithGear(HHGear, cd) then
+            HHGear^.dY := HHGear^.dY + cGravity * 64;
+
+        if (GameFlags and gfMoreWind) <> 0 then
+            // apply wind if there's no obstacle
+            if not TestCollisionXwithGear(HHGear, hwSign(cWindSpeed)) then
+                HHGear^.dX := HHGear^.dX + cWindSpeed * 64 / HHGear^.Density;
+        end
+    else haveCollision:= true;
+
+    if ((Gear^.Message and gmDown) <> 0) and (Gear^.Elasticity < Gear^.Friction) then
+        if not (TestCollisionXwithGear(HHGear, hwSign(ropeDx))
+        or (TestCollisionYwithGear(HHGear, hwSign(ropeDy)) <> 0)) then
+            Gear^.Elasticity := Gear^.Elasticity + _2_4
+    else haveCollision:= true;
+
+    if ((Gear^.Message and gmUp) <> 0) and (Gear^.Elasticity > _30) then
+        if not (TestCollisionXwithGear(HHGear, -hwSign(ropeDx))
+        or (TestCollisionYwithGear(HHGear, -hwSign(ropeDy)) <> 0)) then
+            Gear^.Elasticity := Gear^.Elasticity - _2_4
+    else haveCollision:= true;
+
+(*
+I am not so sure this is useful. Disabling
+    if haveCollision then
+        begin
+        if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) and not TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+            HHGear^.dX.isNegative:= not HHGear^.dX.isNegative;
+        if (TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0) and (TestCollisionYwithGear(HHGear, -hwSign(HHGear^.dY)) = 0) then
+            HHGear^.dY.isNegative:= not HHGear^.dY.isNegative;
+        end;
+*)
+
+    mdX := ropeDx + HHGear^.dX;
+    mdY := ropeDy + HHGear^.dY;
+    len := _1 / Distance(mdX, mdY);
+    // rope vector plus hedgehog direction vector normalized
+    mdX := mdX * len;
+    mdY := mdY * len;
+
+    // for visual purposes only
+    Gear^.dX := mdX;
+    Gear^.dY := mdY;
+
+    /////
+    tx := HHGear^.X;
+    ty := HHGear^.Y;
+
+    HHGear^.X := Gear^.X + mdX * Gear^.Elasticity;
+    HHGear^.Y := Gear^.Y + mdY * Gear^.Elasticity;
+
+    HHGear^.dX := HHGear^.X - tx;
+    HHGear^.dY := HHGear^.Y - ty;
+    ////
+
+
+    haveDivided := false;
+    // check whether rope needs dividing
+
+    len := Gear^.Elasticity - _5;
+    nx := Gear^.X + mdX * len;
+    ny := Gear^.Y + mdY * len;
+    tx := mdX * _2_4; // should be the same as increase step
+    ty := mdY * _2_4;
+
+    while len > _3 do
+        begin
+        lx := hwRound(nx);
+        ly := hwRound(ny);
+        if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and ((Land[ly, lx] and $FF00) <> 0) then
+            begin
+            ny := _1 / Distance(ropeDx, ropeDy);
+            // old rope pos
+            nx := ropeDx * ny;
+            ny := ropeDy * ny;
+
+            with RopePoints.ar[RopePoints.Count] do
+                begin
+                X := Gear^.X;
+                Y := Gear^.Y;
+                if RopePoints.Count = 0 then
+                    RopePoints.HookAngle := DxDy2Angle(Gear^.dY, Gear^.dX);
+                b := (nx * HHGear^.dY) > (ny * HHGear^.dX);
+                dLen := len
+                end;
+                
+            with RopePoints.rounded[RopePoints.Count] do
+                begin
+                X := hwRound(Gear^.X);
+                Y := hwRound(Gear^.Y);
+                end;
+
+            Gear^.X := Gear^.X + nx * len;
+            Gear^.Y := Gear^.Y + ny * len;
+            inc(RopePoints.Count);
+            TryDo(RopePoints.Count <= MAXROPEPOINTS, 'Rope points overflow', true);
+            Gear^.Elasticity := Gear^.Elasticity - len;
+            Gear^.Friction := Gear^.Friction - len;
+            haveDivided := true;
+            break
+            end;
+        nx := nx - tx;
+        ny := ny - ty;
+
+        // len := len - _2_4 // should be the same as increase step
+        len.QWordValue := len.QWordValue - _2_4.QWordValue;
+        end;
+
+    if not haveDivided then
+        if RopePoints.Count > 0 then // check whether the last dividing point could be removed
+            begin
+            tx := RopePoints.ar[Pred(RopePoints.Count)].X;
+            ty := RopePoints.ar[Pred(RopePoints.Count)].Y;
+            mdX := tx - Gear^.X;
+            mdY := ty - Gear^.Y;
+            if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * (ty - HHGear^.Y) > (tx - HHGear^.X) * mdY) then
+                begin
+                dec(RopePoints.Count);
+                Gear^.X := RopePoints.ar[RopePoints.Count].X;
+                Gear^.Y := RopePoints.ar[RopePoints.Count].Y;
+                Gear^.Elasticity := Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen;
+                Gear^.Friction := Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen;
+
+                // restore hog position
+                len := _1 / Distance(mdX, mdY);
+                mdX := mdX * len;
+                mdY := mdY * len;
+
+                HHGear^.X := Gear^.X - mdX * Gear^.Elasticity;
+                HHGear^.Y := Gear^.Y - mdY * Gear^.Elasticity;
+                end
+            end;
+
+    haveCollision := false;
+    if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+        begin
+        HHGear^.dX := -_0_6 * HHGear^.dX;
+        haveCollision := true
+        end;
+    if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0 then
+        begin
+        HHGear^.dY := -_0_6 * HHGear^.dY;
+        haveCollision := true
+        end;
+
+    if haveCollision and (Gear^.Message and (gmLeft or gmRight) <> 0) and (Gear^.Message and (gmUp or gmDown) <> 0) then
+        begin
+        HHGear^.dX := SignAs(hwAbs(HHGear^.dX) + _1_6, HHGear^.dX);
+        HHGear^.dY := SignAs(hwAbs(HHGear^.dY) + _1_6, HHGear^.dY)
+        end;
+
+    len := hwSqr(HHGear^.dX) + hwSqr(HHGear^.dY);
+    if len > _49 then
+        begin
+        len := _7 / hwSqrt(len);
+        HHGear^.dX := HHGear^.dX * len;
+        HHGear^.dY := HHGear^.dY * len;
+        end;
+
+    haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)]) <> 0);
+
+    if not haveCollision then
+        begin
+        // backup gear location
+        tx:= Gear^.X;
+        ty:= Gear^.Y;
+
+        if RopePoints.Count > 0 then
+            begin
+            // set gear location to the remote end of the rope, the attachment point
+            Gear^.X:= RopePoints.ar[0].X;
+            Gear^.Y:= RopePoints.ar[0].Y;
+            end;
+
+        CheckCollision(Gear);
+        // if we haven't found any collision yet then check the other side too
+        if (Gear^.State and gstCollision) = 0 then
+            begin
+            Gear^.dX.isNegative:= not Gear^.dX.isNegative;
+            Gear^.dY.isNegative:= not Gear^.dY.isNegative;
+            CheckCollision(Gear);
+            Gear^.dX.isNegative:= not Gear^.dX.isNegative;
+            Gear^.dY.isNegative:= not Gear^.dY.isNegative;
+            end;
+
+        haveCollision:= (Gear^.State and gstCollision) <> 0;
+
+        // restore gear location
+        Gear^.X:= tx;
+        Gear^.Y:= ty;
+        end;
+
+    // if the attack key is pressed, lose rope contact as well
+    if (Gear^.Message and gmAttack) <> 0 then
+        haveCollision:= false;
+
+    if not haveCollision then
+        begin
+        if (Gear^.State and gsttmpFlag) <> 0 then
+            begin
+            if Gear^.Hedgehog^.CurAmmoType <> amParachute then
+                RopeWaitCollision(Gear, HHGear)
+            else
+                RopeDeleteMe(Gear, HHGear)
+            end
+        end
+    else
+        if (Gear^.State and gsttmpFlag) = 0 then
+            Gear^.State := Gear^.State or gsttmpFlag;
+end;
+
+procedure RopeRemoveFromAmmo(Gear, HHGear: PGear);
+begin
+    if (Gear^.State and gstAttacked) = 0 then
+        begin
+        OnUsedAmmo(HHGear^.Hedgehog^);
+        Gear^.State := Gear^.State or gstAttacked
+        end;
+    ApplyAmmoChanges(HHGear^.Hedgehog^)
+end;
+
+procedure doStepRopeAttach(Gear: PGear);
+var 
+    HHGear: PGear;
+    tx, ty, tt: hwFloat;
+begin
+    Gear^.X := Gear^.X - Gear^.dX;
+    Gear^.Y := Gear^.Y - Gear^.dY;
+    Gear^.Elasticity := Gear^.Elasticity + _1;
+
+    HHGear := Gear^.Hedgehog^.Gear;
+    DeleteCI(HHGear);
+
+    if (HHGear^.State and gstMoving) <> 0 then
+        begin
+        if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+            SetLittle(HHGear^.dX);
+        if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
+            HHGear^.dY := _0;
+
+        HHGear^.X := HHGear^.X + HHGear^.dX;
+        Gear^.X := Gear^.X + HHGear^.dX;
+
+        if TestCollisionYwithGear(HHGear, 1) <> 0 then
+            begin
+            CheckHHDamage(HHGear);
+            HHGear^.dY := _0
+            //HHGear^.State:= HHGear^.State and (not (gstHHJumping or gstHHHJump));
+            end
+        else
+            begin
+            HHGear^.Y := HHGear^.Y + HHGear^.dY;
+            Gear^.Y := Gear^.Y + HHGear^.dY;
+            HHGear^.dY := HHGear^.dY + cGravity;
+            if (GameFlags and gfMoreWind) <> 0 then
+                HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density
+            end;
+
+        tt := Gear^.Elasticity;
+        tx := _0;
+        ty := _0;
+        while tt > _20 do
+            begin
+            if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] and $FF00) <> 0) then
+                begin
+                Gear^.X := Gear^.X + tx;
+                Gear^.Y := Gear^.Y + ty;
+                Gear^.Elasticity := tt;
+                Gear^.doStep := @doStepRopeWork;
+                Gear^.stepFreq:= 8;
+                PlaySound(sndRopeAttach);
+                with HHGear^ do
+                    begin
+                    dX.QWordValue:= dX.QWordValue shl 3;
+                    dY.QWordValue:= dY.QWordValue shl 3;
+                    State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
+                    Message := Message and (not gmAttack)
+                    end;
+
+                RopeRemoveFromAmmo(Gear, HHGear);
+
+                tt := _0;
+                exit
+                end;
+            tx := tx + Gear^.dX + Gear^.dX;
+            ty := ty + Gear^.dY + Gear^.dY;
+            tt := tt - _2;
+            end;
+        end;
+
+    if Gear^.Elasticity < _20 then Gear^.CollisionMask:= $FF00
+    else Gear^.CollisionMask:= $FF7F;
+    CheckCollision(Gear);
+
+    if (Gear^.State and gstCollision) <> 0 then
+        if Gear^.Elasticity < _10 then
+            Gear^.Elasticity := _10000
+    else
+        begin
+        Gear^.doStep := @doStepRopeWork;
+        Gear^.stepFreq:= 8;
+        PlaySound(sndRopeAttach);
+        with HHGear^ do
+            begin
+            dX.QWordValue:= dX.QWordValue shl 3;
+            dY.QWordValue:= dY.QWordValue shl 3;
+            State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
+            Message := Message and (not gmAttack)
+            end;
+
+        RopeRemoveFromAmmo(Gear, HHGear);
+
+        exit
+        end;
+
+    if (Gear^.Elasticity > Gear^.Friction)
+        or ((Gear^.Message and gmAttack) = 0)
+        or ((HHGear^.State and gstHHDriven) = 0)
+        or (HHGear^.Damage > 0) then
+            begin
+            with Gear^.Hedgehog^.Gear^ do
+                begin
+                State := State and (not gstAttacking);
+                Message := Message and (not gmAttack)
+                end;
+        DeleteGear(Gear);
+        exit;
+        end;
+    if CheckGearDrowning(HHGear) then DeleteGear(Gear)
+end;
+
+procedure doStepRope(Gear: PGear);
+begin
+    Gear^.dX := - Gear^.dX;
+    Gear^.dY := - Gear^.dY;
+    Gear^.doStep := @doStepRopeAttach;
+    PlaySound(sndRopeShot)
+end;
+
+end.
--- a/hedgewars/uGearsHedgehog.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsHedgehog.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -234,9 +234,9 @@
         and ((Gear^.Message and gmLJump) <> 0)
         and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
             begin
-            newDx:= dX / _2; 
-            newDy:= dY / _2;
-            altUse:= true;
+            newDx:= dX / CurAmmoGear^.stepFreq; 
+            newDy:= dY / CurAmmoGear^.stepFreq;
+            altUse:= true
             end
         else
             begin
@@ -260,10 +260,7 @@
                    amPickHammer: newGear:= AddGear(hwRound(lx), hwRound(ly) + cHHRadius, gtPickHammer, 0, _0, _0, 0);
                          amSkip: ParseCommand('/skip', true);
                          amRope: newGear:= AddGear(hwRound(lx), hwRound(ly), gtRope, 0, xx, yy, 0);
-                         amMine: if altUse then
-                                     newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, newDx, newDy, 3000)
-                                 else
-                                     newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
+                         amMine: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
                         amSMine: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSMine,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
                        amDEagle: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0);
                       amSineGun: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0);
@@ -360,6 +357,11 @@
                        amTardis: newGear:= AddGear(hwRound(X), hwRound(Y), gtTardis, 0, _0, _0, 5000);
                        amIceGun: newGear:= AddGear(hwRound(X), hwRound(Y), gtIceGun, 0, _0, _0, 0);
              end;
+             if altUse then
+                begin
+                newGear^.dX:= newDx / newGear^.Density;
+                newGear^.dY:= newDY / newGear^.Density
+                end;
              
              case CurAmmoType of
                       amGrenade, amMolotov, 
@@ -455,11 +457,13 @@
 procedure AfterAttack;
 var s: shortstring;
     a: TAmmoType;
+    HHGear: PGear;
 begin
-with CurrentHedgehog^.Gear^, CurrentHedgehog^ do
+with CurrentHedgehog^ do
     begin
+    HHGear:= Gear;
     a:= CurAmmoType;
-    State:= State and (not gstAttacking);
+    if HHGear <> nil then HHGear^.State:= HHGear^.State and (not gstAttacking);
     if (Ammoz[a].Ammo.Propz and ammoprop_Effect) = 0 then
         begin
         Inc(MultiShootAttacks);
@@ -484,8 +488,8 @@
                     TagTurnTimeLeft:= TurnTimeLeft;
                 TurnTimeLeft:=(Ammoz[a].TimeAfterTurn * cGetAwayTime) div 100;
                 end;
-            if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) then
-                State:= State or gstAttacked;
+            if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) and (HHGear <> nil) then 
+                HHGear^.State:= HHGear^.State or gstAttacked;
             if (Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) <> 0 then
                 ApplyAmmoChanges(CurrentHedgehog^)
             end;
@@ -570,7 +574,6 @@
 var s: shortstring;
     vga: PVisualGear;
 begin
-    PlaySound(sndShotgunReload);
     if cnt <> 0 then AddAmmo(HH, ammo, cnt)
     else AddAmmo(HH, ammo);
 
@@ -612,6 +615,7 @@
 case Gear^.Pos of
        posCaseUtility,
        posCaseAmmo: begin
+                    PlaySound(sndShotgunReload);
                     if Gear^.AmmoType <> amNothing then 
                         begin
                         AddPickup(HH^.Hedgehog^, Gear^.AmmoType, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y));
@@ -634,7 +638,7 @@
                                 end;
                             gi := gi^.NextGear
                             end;
-                        ag:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAddAmmo, gstInvisible, _0, _0, GetRandom(200)+100);
+                        ag:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAddAmmo, gstInvisible, _0, _0, GetRandom(125)+25);
                         ag^.Pos:= Gear^.Pos;
                         ag^.Power:= Gear^.Power
                         end;
@@ -853,7 +857,7 @@
     Gear^.State:= Gear^.State and (not gstMoving);
     exit
     end;
-isFalling:= (Gear^.dY.isNegative) or not TestCollisionYKick(Gear, 1);
+isFalling:= (Gear^.dY.isNegative) or (not TestCollisionYKick(Gear, 1));
 if isFalling then
     begin
     if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then
@@ -983,7 +987,7 @@
     begin
     Gear^.State:= Gear^.State and (not gstWinner);
     Gear^.State:= Gear^.State and (not gstMoving);
-    while (TestCollisionYWithGear(Gear,1) = 0) and not CheckGearDrowning(Gear) do
+    while (TestCollisionYWithGear(Gear,1) = 0) and (not CheckGearDrowning(Gear)) do
         Gear^.Y:= Gear^.Y+_1;
     SetLittle(Gear^.dX);
     Gear^.dY:= _0
@@ -1010,8 +1014,8 @@
         if (CurrentHedgehog^.Gear = Gear) then
             isCursorVisible:= false
     end;
-
-if (hwAbs(Gear^.dY) > _0) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then
+// IMO this should trigger homerun based on leftX/rightX + someval instead - that is 'knocking it out of the park'
+if (not isZero(Gear^.dY)) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then
     begin
     inc(Gear^.FlightTime);
     if Gear^.FlightTime = 3000 then
--- a/hedgewars/uGearsList.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsList.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -105,6 +105,7 @@
 // Define ammo association, if any.
 gear^.AmmoType:= GearKindAmmoTypeMap[Kind];
 gear^.CollisionMask:= $FFFF;
+gear^.stepFreq:= 1;
 
 if CurrentHedgehog <> nil then gear^.Hedgehog:= CurrentHedgehog;
 
@@ -230,7 +231,7 @@
                 gear^.Radius:= 2;
                 gear^.Elasticity:= _0_55;
                 gear^.Friction:= _0_995;
-                gear^.Density:= _0_9;
+                gear^.Density:= _1;
                 if cMinesTime < 0 then
                     gear^.Timer:= getrandom(51)*100
                 else
@@ -242,7 +243,7 @@
                 gear^.Radius:= 2;
                 gear^.Elasticity:= _0_55;
                 gear^.Friction:= _0_995;
-                gear^.Density:= _0_9;
+                gear^.Density:= _1_6;
                 gear^.Timer:= 500;
                 end;
         gtCase: begin
--- a/hedgewars/uGearsRender.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsRender.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -376,26 +376,26 @@
                         hAngle:= 0;
                         i:= -1
                         end;
-                if ((Gear^.State and gstWinner) = 0) then
-                    begin
-                    DrawHedgehog(ox, oy,
-                            i,
-                            1,
-                            0,
-                            DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + dAngle);
-                    with HH^ do
-                        if (HatTex <> nil) then
-                            begin
-                            DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 0, i, 32, 32,
-                                i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
-                            if HatTex^.w > 64 then
+                    if ((Gear^.State and gstWinner) = 0) then
+                        begin
+                        DrawHedgehog(ox, oy,
+                                i,
+                                1,
+                                0,
+                                DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + dAngle);
+                        with HH^ do
+                            if (HatTex <> nil) then
                                 begin
-                                Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
-                                DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 32, i, 32, 32,
+                                DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 0, i, 32, 32,
                                     i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
-                                Tint($FF, $FF, $FF, $FF)
+                                if HatTex^.w > 64 then
+                                    begin
+                                    Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
+                                    DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 32, i, 32, 32,
+                                        i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
+                                    Tint($FF, $FF, $FF, $FF)
+                                    end
                                 end
-                            end
                     end;
                     DrawAltWeapon(Gear, ox, oy);
                     defaultPos:= false
--- a/hedgewars/uGearsUtils.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsUtils.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -38,6 +38,8 @@
 
 function  CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
 function  CheckGearDrowning(Gear: PGear): boolean;
+procedure CheckCollision(Gear: PGear); inline;
+procedure CheckCollisionWithLand(Gear: PGear); inline;
 
 var doStepHandlers: array[TGearType] of TGearStepProcedure;
 
@@ -106,7 +108,7 @@
 // Run the calcs only once we know we have a type that will need damage
                         tdX:= Gear^.X-fX;
                         tdY:= Gear^.Y-fY;
-                        if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then
+                        if (tdX.Round + tdY.Round + 2) < dmgBase then
                             dmg:= dmgBase - hwRound(Distance(tdX, tdY));
                         if dmg > 1 then
                             begin
@@ -140,7 +142,7 @@
 // Run the calcs only once we know we have a type that will need damage
                         tdX:= Gear^.X-fX;
                         tdY:= Gear^.Y-fY;
-                        if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then
+                        if (tdX.Round + tdY.Round + 2) < dmgBase then
                             dmg:= dmgBase - hwRound(Distance(tdX, tdY));
                         if dmg > 1 then
                             begin
@@ -239,9 +241,7 @@
         end;
     end
     else if Gear^.Kind <> gtStructure then // not gtHedgehog nor gtStructure
-        begin
         Gear^.Hedgehog:= AttackerHog;
-        end;
     inc(Gear^.Damage, Damage);
     
     ScriptCall('onGearDamage', Gear^.UID, Damage);
@@ -319,7 +319,8 @@
 var 
     dAngle: real;
 begin
-    dAngle := (Gear^.dX.QWordValue + Gear^.dY.QWordValue) / $80000000;
+// Frac/Round to be kind to JS as of 2012-08-27 where there is yet no int64/uint64
+    dAngle := (Gear^.dX.Round + Gear^.dY.Round) / 2 + (Gear^.dX.Frac+Gear^.dY.Frac) / $80000000;
     if not Gear^.dX.isNegative then
         Gear^.DirAngle := Gear^.DirAngle + dAngle
     else
@@ -420,10 +421,9 @@
                 Scale:= hwFloat2Float(Gear^.Density / _3 * Gear^.dY);
                 if Scale > 1 then Scale:= power(Scale,0.3333)
                 else Scale:= Scale + ((1-Scale) / 2);
-                if Scale > 1 then Timer:= round(max(Scale,3))
+                if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
                 else Timer:= 1;
                 // Low Gravity
-                Timer:=round(0.0005/cGravityf);
                 FrameTicks:= FrameTicks*Timer;
                 end;
 
@@ -604,7 +604,7 @@
                     inc(cnt)
                     end;
 
-                inc(y, 45)
+                inc(y, 10)
                 end;
 
             if cnt > 0 then
@@ -664,4 +664,22 @@
 CheckGearNear:= nil
 end;
 
+procedure CheckCollision(Gear: PGear); inline;
+begin
+    if TestCollisionXwithGear(Gear, hwSign(Gear^.dX))
+    or (TestCollisionYwithGear(Gear, hwSign(Gear^.dY)) <> 0) then
+        Gear^.State := Gear^.State or gstCollision
+    else
+        Gear^.State := Gear^.State and (not gstCollision)
+end;
+
+procedure CheckCollisionWithLand(Gear: PGear); inline;
+begin
+    if TestCollisionX(Gear, hwSign(Gear^.dX))
+    or TestCollisionY(Gear, hwSign(Gear^.dY)) then
+        Gear^.State := Gear^.State or gstCollision
+    else 
+        Gear^.State := Gear^.State and (not gstCollision)
+end;
+
 end.
--- a/hedgewars/uIO.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uIO.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -126,6 +126,7 @@
                'D': GameType:= gmtDemo;
                'N': GameType:= gmtNet;
                'S': GameType:= gmtSave;
+               'V': GameType:= gmtRecord;
                else OutError(errmsgIncorrectUse + ' IPC "T" :' + s[2], true) end;
      'V': begin
               if s[2] = '.' then
@@ -406,7 +407,7 @@
             TargetPoint.Y:= putY
             end;
         AddFileLog('put: ' + inttostr(TargetPoint.X) + ', ' + inttostr(TargetPoint.Y));
-        State:= State and not gstHHChooseTarget;
+        State:= State and (not gstHHChooseTarget);
         if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackingPut) <> 0 then
             Message:= Message or (gmAttack and InputMask);
         end
--- a/hedgewars/uInputHandler.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uInputHandler.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -25,8 +25,9 @@
 procedure initModule;
 procedure freeModule;
 
-function  KeyNameToCode(name: shortstring; Modifier: shortstring = ''): LongInt;
-procedure MaskModifier(var code: LongInt; modifier: LongWord);
+function  KeyNameToCode(name: shortstring): LongInt; inline;
+function  KeyNameToCode(name: shortstring; Modifier: shortstring): LongInt;
+//procedure MaskModifier(var code: LongInt; modifier: LongWord);
 procedure MaskModifier(Modifier: shortstring; var code: LongInt);
 procedure ProcessMouse(event: TSDL_MouseButtonEvent; ButtonDown: boolean);
 procedure ProcessKey(event: TSDL_KeyboardEvent); inline;
@@ -60,6 +61,11 @@
     KeyNames: array [0..cKeyMaxIndex] of string[15];
     CurrentBinds: TBinds;
 
+function  KeyNameToCode(name: shortstring): LongInt; inline;
+begin
+    KeyNameToCode:= KeyNameToCode(name, '');
+end;
+
 function KeyNameToCode(name: shortstring; Modifier: shortstring): LongInt;
 var code: LongInt;
 begin
@@ -70,7 +76,7 @@
     MaskModifier(Modifier, code);
     KeyNameToCode:= code;
 end;
-
+(*
 procedure MaskModifier(var code: LongInt; Modifier: LongWord);
 begin
     if(Modifier and KMOD_LSHIFT) <> 0 then code:= code or LSHIFT; 
@@ -80,7 +86,7 @@
     if(Modifier and KMOD_LCTRL) <> 0 then code:= code or LCTRL; 
     if(Modifier and KMOD_RCTRL) <> 0 then code:= code or LCTRL; 
 end;
-
+*)
 procedure MaskModifier(Modifier: shortstring; var code: LongInt);
 var mod_ : shortstring;
     ModifierCount, i: LongInt;
@@ -133,7 +139,7 @@
 
 if CurrentBinds[code][0] <> #0 then
     begin
-    if (code > 3) and KeyDown and not ((CurrentBinds[code] = 'put') or (CurrentBinds[code] = 'ammomenu') or (CurrentBinds[code] = '+cur_u') or (CurrentBinds[code] = '+cur_d') or (CurrentBinds[code] = '+cur_l') or (CurrentBinds[code] = '+cur_r')) then hideAmmoMenu:= true;
+    if (code > 3) and KeyDown and (not ((CurrentBinds[code] = 'put')) or (CurrentBinds[code] = 'ammomenu') or (CurrentBinds[code] = '+cur_u') or (CurrentBinds[code] = '+cur_d') or (CurrentBinds[code] = '+cur_l') or (CurrentBinds[code] = '+cur_r')) then hideAmmoMenu:= true;
 
     if KeyDown then
         begin
@@ -250,6 +256,7 @@
 DefaultBinds[KeyNameToCode(_S'9')]:= '+voldown';
 DefaultBinds[KeyNameToCode(_S'8')]:= 'mute';
 DefaultBinds[KeyNameToCode(_S'c')]:= 'capture';
+DefaultBinds[KeyNameToCode(_S'r')]:= 'record';
 DefaultBinds[KeyNameToCode(_S'h')]:= 'findhh';
 DefaultBinds[KeyNameToCode(_S'p')]:= 'pause';
 DefaultBinds[KeyNameToCode(_S's')]:= '+speedup';
--- a/hedgewars/uLand.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLand.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -20,7 +20,7 @@
 
 unit uLand;
 interface
-uses SDLh, uLandTemplates, uFloat, uConsts, GLunit, uTypes;
+uses SDLh, uLandTemplates, uFloat, uConsts, GLunit, uTypes, uAILandMarks;
 
 procedure initModule;
 procedure freeModule;
@@ -35,6 +35,28 @@
 
 var digest: shortstring;
 
+procedure ResizeLand(width, height: LongWord);
+var potW, potH: LongWord;
+begin 
+potW:= toPowerOf2(width);
+potH:= toPowerOf2(height);
+if (potW <> LAND_WIDTH) or (potH <> LAND_HEIGHT) then
+    begin
+    LAND_WIDTH:= potW;
+    LAND_HEIGHT:= potH;
+    LAND_WIDTH_MASK:= not(LAND_WIDTH-1);
+    LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1);
+    cWaterLine:= LAND_HEIGHT;
+    if (cReducedQuality and rqBlurryLand) = 0 then
+        SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH)
+    else
+        SetLength(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2);
+
+    SetLength(Land, LAND_HEIGHT, LAND_WIDTH);
+    SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32));
+    end;
+end;
+
 procedure ColorizeLand(Surface: PSDL_Surface);
 var tmpsurf: PSDL_Surface;
     r, rr: TSDL_Rect;
@@ -181,6 +203,7 @@
     i: Longword;
     y, x: Longword;
 begin
+    ResizeLand(Template.TemplateWidth, Template.TemplateHeight);
     for y:= 0 to LAND_HEIGHT - 1 do
         for x:= 0 to LAND_WIDTH - 1 do
             Land[y, x]:= lfBasic;
@@ -237,6 +260,7 @@
 
 procedure GenDrawnMap;
 begin
+    ResizeLand(4096, 2048);
     uLandPainted.Draw;
 
     MaxHedgehogs:= 48;
@@ -260,6 +284,11 @@
         3: SelectTemplate:= LargeTemplates[getrandom(Succ(High(LargeTemplates)))];
         4: SelectTemplate:= CavernTemplates[getrandom(Succ(High(CavernTemplates)))];
         5: SelectTemplate:= WackyTemplates[getrandom(Succ(High(WackyTemplates)))];
+// For lua only!
+        6: begin
+           SelectTemplate:= min(LuaTemplateNumber,High(EdgeTemplates));
+           GetRandom(2) // burn 1
+           end;
     end;
 
     WriteLnToConsole('Selected template #'+inttostr(SelectTemplate)+' using filter #'+inttostr(cTemplateFilter));
@@ -299,7 +328,7 @@
     WriteLnToConsole('Generating land...');
     case cMapGen of
         0: GenBlank(EdgeTemplates[SelectTemplate]);
-        1: GenMaze;
+        1: begin ResizeLand(4096,2048); GenMaze; end;
         2: GenDrawnMap;
     else
         OutError('Unknown mapgen', true);
@@ -489,7 +518,10 @@
     if tmpsurf = nil then
         tmpsurf:= LoadImage(Pathz[ptMissionMaps] + '/' + mapName + '/map', ifAlpha or ifCritical or ifTransparent or ifIgnoreCaps);
     end;
-TryDo((tmpsurf^.w <= LAND_WIDTH) and (tmpsurf^.h <= LAND_HEIGHT), 'Map dimensions too big!', true);
+// (bare) Sanity check. Considering possible LongInt comparisons as well as just how much system memoery it would take
+TryDo((tmpsurf^.w < $40000000) and (tmpsurf^.h < $40000000) and (tmpsurf^.w * tmpsurf^.h < 6*1024*1024*1024), 'Map dimensions too big!', true);
+
+ResizeLand(tmpsurf^.w, tmpsurf^.h);
 
 // unC0Rr - should this be passed from the GUI? I am not sure which layer does what
 s:= UserPathz[ptMapCurrent] + '/map.cfg';
@@ -581,7 +613,7 @@
             if Land[y, x] <> 0 then
                 begin
                 inc(c);
-                if c > 200 then // avoid accidental triggering
+                if c > 1000 then // avoid accidental triggering
                     begin
                     hasBorder:= true;
                     break;
@@ -676,24 +708,35 @@
                 LandPixels[y,x]:= w or (LandPixels[y div 2, x div 2] and AMask)
                 end
     end;
-
-UpdateLandTexture(0, LAND_WIDTH, 0, LAND_HEIGHT, false);
 end;
 
 procedure GenPreview(out Preview: TPreview);
-var x, y, xx, yy, t, bit, cbit, lh, lw: LongInt;
+var rh, rw, ox, oy, x, y, xx, yy, t, bit, cbit, lh, lw: LongInt;
 begin
     WriteLnToConsole('Generating preview...');
     case cMapGen of
         0: GenBlank(EdgeTemplates[SelectTemplate]);
-        1: GenMaze;
+        1: begin ResizeLand(4096,2048); GenMaze; end;
         2: GenDrawnMap;
     else
         OutError('Unknown mapgen', true);
     end;
 
-    lh:= LAND_HEIGHT div 128;
-    lw:= LAND_WIDTH div 32;
+    // strict scaling needed here since preview assumes a rectangle
+    rh:= max(LAND_HEIGHT,2048);
+    rw:= max(LAND_WIDTH,4096);
+    ox:= 0;
+    if rw < rh*2 then
+        begin
+        rw:= rh*2;
+        end;
+    if rh < rw div 2 then rh:= rw * 2;
+    
+    ox:= (rw-LAND_WIDTH) div 2;
+    oy:= rh-LAND_HEIGHT;
+
+    lh:= rh div 128;
+    lw:= rw div 32;
     for y:= 0 to 127 do
         for x:= 0 to 31 do
         begin
@@ -704,7 +747,8 @@
                 cbit:= bit * 8;
                 for yy:= y * lh to y * lh + 7 do
                     for xx:= x * lw + cbit to x * lw + cbit + 7 do
-                        if Land[yy, xx] <> 0 then
+                        if ((yy-oy) and LAND_HEIGHT_MASK = 0) and ((xx-ox) and LAND_WIDTH_MASK = 0) 
+                           and (Land[yy-oy, xx-ox] <> 0) then
                             inc(t);
                 if t > 8 then
                     Preview[y, x]:= Preview[y, x] or ($80 shr bit);
@@ -741,7 +785,9 @@
 
     LandBackSurface:= nil;
     digest:= '';
-
+    LAND_WIDTH:= 0;
+    LAND_HEIGHT:= 0;
+(*
     if (cReducedQuality and rqBlurryLand) = 0 then
         SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH)
     else
@@ -749,6 +795,7 @@
 
     SetLength(Land, LAND_HEIGHT, LAND_WIDTH);
     SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32));
+*)
 end;
 
 procedure freeModule;
--- a/hedgewars/uLandGraphics.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandGraphics.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -59,7 +59,7 @@
         begin
         addBgColor:= NewColor;
         exit
-        end; 
+        end;
     // Get colors
     oRed   := (OldColor shr RShift);
     oGreen := (OldColor shr GShift);
@@ -72,7 +72,7 @@
     // Mix colors
     nRed   := min(255,((nRed*nAlpha) div 255) + ((oRed*oAlpha*byte(255-nAlpha)) div 65025));
     nGreen := min(255,((nGreen*nAlpha) div 255) + ((oGreen*oAlpha*byte(255-nAlpha)) div 65025));
-    nBlue  := min(255,((nBlue*nAlpha) div 255) + ((oBlue*oAlpha*byte(255-nAlpha)) div 65025)); 
+    nBlue  := min(255,((nBlue*nAlpha) div 255) + ((oBlue*oAlpha*byte(255-nAlpha)) div 65025));
     nAlpha := min(255, oAlpha + nAlpha);
 
     addBgColor := (nAlpha shl AShift) or (nRed shl RShift) or (nGreen shl GShift) or (nBlue shl BShift);
@@ -106,25 +106,25 @@
     begin
     if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if isCurrent then 
+            if isCurrent then
                 Land[y + dy, i]:= Land[y + dy, i] and $FF7F
             else if Land[y + dy, i] and $007F > 0 then
                 Land[y + dy, i]:= (Land[y + dy, i] and $FF80) or ((Land[y + dy, i] and $7F) - 1);
     if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if isCurrent then 
+            if isCurrent then
                 Land[y - dy, i]:= Land[y - dy, i] and $FF7F
             else if Land[y - dy, i] and $007F > 0 then
                 Land[y - dy, i]:= (Land[y - dy, i] and $FF80) or ((Land[y - dy, i] and $7F) - 1);
     if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if isCurrent then 
+            if isCurrent then
                 Land[y + dx, i]:= Land[y + dx, i] and $FF7F
             else if Land[y + dx, i] and $007F > 0 then
                 Land[y + dx, i]:= (Land[y + dx, i] and $FF80) or ((Land[y + dx, i] and $7F) - 1);
     if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if isCurrent then 
+            if isCurrent then
                 Land[y - dx, i]:= Land[y - dx, i] and $FF7F
             else if Land[y - dx, i] and $007F > 0 then
                 Land[y - dx, i]:= (Land[y - dx, i] and $FF80) or ((Land[y - dx, i] and $7F) - 1)
@@ -133,27 +133,27 @@
     begin
     if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if isCurrent then 
+            if isCurrent then
                 Land[y + dy, i]:= Land[y + dy, i] or $80
             else if Land[y + dy, i] and $007F < 127 then
                 Land[y + dy, i]:= (Land[y + dy, i] and $FF80) or ((Land[y + dy, i] and $7F) + 1);
-    if ((y - dy) and LAND_HEIGHT_MASK) = 0 then                                                   
-        for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do                                  
-            if isCurrent then                                                                     
-                Land[y - dy, i]:= Land[y - dy, i] or $80                                          
-            else if Land[y - dy, i] and $007F < 127 then                                          
+    if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
+        for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
+            if isCurrent then
+                Land[y - dy, i]:= Land[y - dy, i] or $80
+            else if Land[y - dy, i] and $007F < 127 then
                 Land[y - dy, i]:= (Land[y - dy, i] and $FF80) or ((Land[y - dy, i] and $7F) + 1);
-    if ((y + dx) and LAND_HEIGHT_MASK) = 0 then                                                   
-        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do                                  
-            if isCurrent then                                                                     
-                Land[y + dx, i]:= Land[y + dx, i] or $80                                          
-            else if Land[y + dx, i] and $007F < 127 then                                          
+    if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
+        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
+            if isCurrent then
+                Land[y + dx, i]:= Land[y + dx, i] or $80
+            else if Land[y + dx, i] and $007F < 127 then
                 Land[y + dx, i]:= (Land[y + dx, i] and $FF80) or ((Land[y + dx, i] and $7F) + 1);
-    if ((y - dx) and LAND_HEIGHT_MASK) = 0 then                                                   
-        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do                                  
-            if isCurrent then                                                                     
-                Land[y - dx, i]:= Land[y - dx, i] or $80                                          
-            else if Land[y - dx, i] and $007F < 127 then                                          
+    if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
+        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
+            if isCurrent then
+                Land[y - dx, i]:= Land[y - dx, i] or $80
+            else if Land[y - dx, i] and $007F < 127 then
                 Land[y - dx, i]:= (Land[y - dx, i] and $FF80) or ((Land[y - dx, i] and $7F) + 1)
     end
 end;
@@ -266,7 +266,7 @@
                 inc(cnt);
                 LandPixels[by, bx]:= LandBackPixel(i, t)
                 end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then 
+            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
                 LandPixels[by, bx]:= 0
             end;
 
@@ -288,7 +288,7 @@
                 inc(cnt);
                 LandPixels[by, bx]:= LandBackPixel(i, t)
                 end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then 
+            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
                 LandPixels[by, bx]:= 0
             end;
 
@@ -310,7 +310,7 @@
                 inc(cnt);
                 LandPixels[by, bx]:= LandBackPixel(i, t)
                 end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then 
+            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
                 LandPixels[by, bx]:= 0
             end;
 t:= y - dx;
@@ -331,7 +331,7 @@
                 inc(cnt);
                 LandPixels[by, bx]:= LandBackPixel(i, t)
                 end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then 
+            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
                 LandPixels[by, bx]:= 0
             end;
 FillLandCircleLinesBG:= cnt;
@@ -504,7 +504,7 @@
                     end;
                 if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
                     LandPixels[by, bx]:= LandBackPixel(tx, ty)
-                else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then 
+                else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
                     LandPixels[by, bx]:= 0
                 end
             end;
@@ -567,7 +567,7 @@
     and ((tx and LAND_WIDTH_MASK) = 0)
     and (((Land[ty, tx] and lfBasic) <> 0) or ((Land[ty, tx] and lfObject) <> 0)) then
         begin
-        if despeckle then 
+        if despeckle then
             begin
             Land[ty, tx]:= Land[ty, tx] or lfDamaged;
             LandDirty[ty div 32, tx div 32]:= 1
@@ -837,7 +837,7 @@
 procedure Smooth(X, Y: LongInt);
 begin
 // a bit of AA for explosions
-if (Land[Y, X] = 0) and (Y > LongInt(topY) + 1) and 
+if (Land[Y, X] = 0) and (Y > LongInt(topY) + 1) and
     (Y < LAND_HEIGHT-2) and (X > LongInt(leftX) + 1) and (X < LongInt(rightX) - 1) then
     begin
     if ((((Land[y, x-1] and lfDamaged) <> 0) and (((Land[y+1,x] and lfDamaged) <> 0)) or ((Land[y-1,x] and lfDamaged) <> 0))
--- a/hedgewars/uLandObjects.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandObjects.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -501,7 +501,7 @@
             c2.g:= t;
             c2.b:= t
             end;
-        ExplosionBorderColor:= c2.value or AMask;
+        ExplosionBorderColor:= (c2.r shl RShift) or (c2.g shl GShift) or (c2.b shl BShift) or AMask; 
         end
     else if key = 'water-top' then
         begin
@@ -554,7 +554,7 @@
         SetMusicName(Trim(s))
     else if key = 'clouds' then
         begin
-        cCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div LAND_WIDTH;
+        cCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div 4096;
         cSDCloudsNumber:= cCloudsNumber
         end
     else if key = 'object' then
@@ -700,7 +700,7 @@
     else if key = 'sd-water-opacity' then
         SDWaterOpacity:= StrToInt(Trim(s))
     else if key = 'sd-clouds' then
-        cSDCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div LAND_WIDTH
+        cSDCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div 4096
     else if key = 'sd-flakes' then
         begin
         i:= Pos(',', s);
--- a/hedgewars/uLandPainted.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandPainted.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -58,9 +58,9 @@
         rec.X:= SDLNet_Read16(@rec.X);
         rec.Y:= SDLNet_Read16(@rec.Y);
         if rec.X < -318 then rec.X:= -318;
-        if rec.X > LAND_WIDTH+318 then rec.X:= LAND_WIDTH+318;
+        if rec.X > 4096+318 then rec.X:= 4096+318;
         if rec.Y < -318 then rec.Y:= -318;
-        if rec.Y > LAND_HEIGHT+318 then rec.Y:= LAND_HEIGHT+318;
+        if rec.Y > 2048+318 then rec.Y:= 2048+318;
 
         new(pe);
         if pointsListLast = nil then
--- a/hedgewars/uLandTemplates.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandTemplates.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -1457,92 +1457,92 @@
 // Many islands
 const Template41Points: array[0..86] of TSDL_Rect =
       (
-       (x:   95; y: 500; w:   1; h:   1),
-       (x:  100; y: 275; w:  25; h: 100),
-       (x:  325; y: 275; w:  25; h: 100),
-       (x:  330; y: 500; w:   1; h:   1),
+       (x:   95; y: 500; w:  26; h:  26),
+       (x:  100; y: 275; w:  50; h: 125),
+       (x:  325; y: 275; w:  50; h: 125),
+       (x:  330; y: 500; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x:  725; y: 125; w:   1; h:   1),
-       (x:  725; y:  25; w:   5; h:  25),
-       (x:  825; y:  35; w:   5; h:  10),
-       (x:  825; y: 135; w:   1; h:   1),
+       (x:  725; y: 125; w:  26; h:  26),
+       (x:  725; y:  25; w:  30; h:  50),
+       (x:  825; y:  35; w:  30; h:  35),
+       (x:  825; y: 135; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 1150; y: 550; w:  25; h:  50),
-       (x: 1250; y: 300; w:  25; h:  50),
-       (x: 1350; y: 300; w:  25; h:  50),
-       (x: 1400; y: 575; w:  25; h:  50),
+       (x: 1150; y: 550; w:  50; h:  75),
+       (x: 1250; y: 300; w:  50; h:  75),
+       (x: 1350; y: 300; w:  50; h:  75),
+       (x: 1400; y: 575; w:  50; h:  75),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x:  525; y:1050; w:  50; h:  50),
-       (x:  700; y: 800; w: 100; h: 150),
-       (x:  950; y: 900; w: 100; h: 150),
-       (x: 1100; y:1100; w:  50; h:  50),
+       (x:  525; y:1050; w:  75; h:  75),
+       (x:  700; y: 800; w: 125; h: 175),
+       (x:  950; y: 900; w: 125; h: 175),
+       (x: 1100; y:1100; w:  75; h:  75),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x:  175; y:1500; w:   1; h:   1),
-       (x:  210; y:1400; w:   5; h:  25),
-       (x:  240; y:1400; w:   5; h:  25),
-       (x:  275; y:1510; w:   1; h:   1),
+       (x:  175; y:1500; w:  26; h:  26),
+       (x:  210; y:1400; w:  30; h:  50),
+       (x:  240; y:1400; w:  30; h:  50),
+       (x:  275; y:1510; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x:  450; y:1800; w: 100; h: 100),
-       (x:  600; y:1750; w: 100; h: 100),
-       (x:  750; y:1750; w: 100; h: 100),
-       (x:  950; y:1850; w: 100; h: 100),
+       (x:  450; y:1800; w: 125; h: 125),
+       (x:  600; y:1750; w: 125; h: 125),
+       (x:  750; y:1750; w: 125; h: 125),
+       (x:  950; y:1850; w: 125; h: 125),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 1075; y:1450; w:   1; h:   1),
-       (x: 1110; y:1300; w:   5; h:  25),
-       (x: 1140; y:1300; w:   5; h:  25),
-       (x: 1175; y:1430; w:   1; h:   1),
+       (x: 1075; y:1450; w:  26; h:  26),
+       (x: 1110; y:1300; w:  30; h:  50),
+       (x: 1140; y:1300; w:  30; h:  50),
+       (x: 1175; y:1430; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 1600; y:1250; w:  25; h: 100),
-       (x: 1700; y:1150; w:  25; h: 100),
-       (x: 1850; y: 500; w:  50; h: 100),
-       (x: 1950; y: 550; w:  50; h: 150),
-       (x: 2250; y:1150; w:  25; h: 100),
-       (x: 2350; y:1250; w:  25; h: 100),
+       (x: 1600; y:1250; w:  50; h: 125),
+       (x: 1700; y:1150; w:  50; h: 125),
+       (x: 1850; y: 500; w:  75; h: 125),
+       (x: 1950; y: 550; w:  75; h: 175),
+       (x: 2250; y:1150; w:  50; h: 125),
+       (x: 2350; y:1250; w:  50; h: 125),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 1750; y:2010; w:   1; h:   1),
-       (x: 1900; y:1870; w:  50; h:  50),
-       (x: 2050; y:1870; w:  50; h:  50),
-       (x: 2175; y:2010; w:   1; h:   1),
+       (x: 1750; y:2010; w:  26; h:  26),
+       (x: 1900; y:1870; w:  75; h:  75),
+       (x: 2050; y:1870; w:  75; h:  75),
+       (x: 2175; y:2010; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 2500; y:1700; w:   1; h:   1),
-       (x: 2575; y:1500; w:  10; h:  50),
-       (x: 2650; y:1500; w:  10; h:  50),
-       (x: 2700; y:1690; w:   1; h:   1),
+       (x: 2500; y:1700; w:  26; h:  26),
+       (x: 2575; y:1500; w:  35; h:  75),
+       (x: 2650; y:1500; w:  35; h:  75),
+       (x: 2700; y:1690; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 2000; y: 125; w:   1; h:   1),
-       (x: 2050; y:  50; w:  25; h:  25),
-       (x: 2100; y:  50; w:  25; h:  25),
-       (x: 2150; y: 150; w:   1; h:   1),
+       (x: 2000; y: 125; w:  26; h:  26),
+       (x: 2050; y:  50; w:  50; h:  50),
+       (x: 2100; y:  50; w:  50; h:  50),
+       (x: 2150; y: 150; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 2600; y: 250; w:  25; h: 100),
-       (x: 2750; y: 400; w:  50; h:  50),
-       (x: 2900; y: 525; w:  50; h:  50),
-       (x: 3150; y: 550; w:  50; h: 100),
+       (x: 2600; y: 250; w:  50; h: 125),
+       (x: 2750; y: 400; w:  75; h:  75),
+       (x: 2900; y: 525; w:  75; h:  75),
+       (x: 3150; y: 550; w:  75; h: 125),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 2800; y:1150; w:   1; h:   1),
-       (x: 2840; y: 950; w:  25; h:  25),
-       (x: 2880; y: 950; w:  25; h:  25),
-       (x: 2900; y:1150; w:   1; h:   1),
+       (x: 2800; y:1150; w:  26; h:  26),
+       (x: 2840; y: 950; w:  50; h:  50),
+       (x: 2880; y: 950; w:  50; h:  50),
+       (x: 2900; y:1150; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 3075; y:1985; w:   1; h:   1),
-       (x: 3325; y:1700; w:  50; h: 100),
-       (x: 3475; y:1700; w:  50; h: 100),
-       (x: 3625; y:1985; w:   1; h:   1),
+       (x: 3075; y:1985; w:  26; h:  26),
+       (x: 3325; y:1700; w:  75; h: 125),
+       (x: 3475; y:1700; w:  75; h: 125),
+       (x: 3625; y:1985; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 3200; y:1450; w:   1; h:   1),
-       (x: 3240; y:1350; w:  25; h:  25),
-       (x: 3280; y:1350; w:  25; h:  25),
-       (x: 3300; y:1450; w:   1; h:   1),
+       (x: 3200; y:1450; w:  26; h:  26),
+       (x: 3240; y:1350; w:  50; h:  50),
+       (x: 3280; y:1350; w:  50; h:  50),
+       (x: 3300; y:1450; w:  26; h:  26),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 3500; y:1050; w:  25; h:  50),
-       (x: 3650; y: 600; w:  50; h: 100),
-       (x: 3800; y: 600; w:  50; h: 100),
-       (x: 3900; y:1000; w:  25; h:  50),
+       (x: 3500; y:1050; w:  50; h:  75),
+       (x: 3650; y: 600; w:  75; h: 125),
+       (x: 3800; y: 600; w:  75; h: 125),
+       (x: 3900; y:1000; w:  50; h:  75),
        (x: NTPX; y:   0; w:   1; h:   1),
-       (x: 3800; y: 200; w:  25; h:  50),
-       (x: 3875; y: 100; w:  50; h:  50),
-       (x: 3925; y:  50; w:  50; h:  25),
-       (x: 4050; y: 125; w:  25; h:  50),
+       (x: 3800; y: 200; w:  50; h:  75),
+       (x: 3875; y: 100; w:  75; h:  75),
+       (x: 3925; y:  50; w:  75; h:  50),
+       (x: 4050; y: 125; w:  50; h:  75),
        (x: NTPX; y:   0; w:   1; h:   1)
       );
       Template41FPoints: array[0..0] of TPoint =
@@ -1571,9 +1571,191 @@
       (
        (X: 512; Y:    0)
       );
+// Many islands
+const Template43Points: array[0..173] of TSDL_Rect =
+      (
+       (x:   95; y: 500; w:  26; h:  26),
+       (x:  100; y: 275; w:  50; h: 125),
+       (x:  325; y: 275; w:  50; h: 125),
+       (x:  330; y: 500; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x:  725; y: 125; w:  26; h:  26),
+       (x:  725; y:  25; w:  30; h:  50),
+       (x:  825; y:  35; w:  30; h:  35),
+       (x:  825; y: 135; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 1150; y: 550; w:  50; h:  75),
+       (x: 1250; y: 300; w:  50; h:  75),
+       (x: 1350; y: 300; w:  50; h:  75),
+       (x: 1400; y: 575; w:  50; h:  75),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x:  525; y:1050; w:  75; h:  75),
+       (x:  700; y: 800; w: 125; h: 175),
+       (x:  950; y: 900; w: 125; h: 175),
+       (x: 1100; y:1100; w:  75; h:  75),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x:  175; y:1500; w:  26; h:  26),
+       (x:  210; y:1400; w:  30; h:  50),
+       (x:  240; y:1400; w:  30; h:  50),
+       (x:  275; y:1510; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x:  450; y:1800; w: 125; h: 125),
+       (x:  600; y:1750; w: 125; h: 125),
+       (x:  750; y:1750; w: 125; h: 125),
+       (x:  950; y:1850; w: 125; h: 125),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 1075; y:1450; w:  26; h:  26),
+       (x: 1110; y:1300; w:  30; h:  50),
+       (x: 1140; y:1300; w:  30; h:  50),
+       (x: 1175; y:1430; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 1600; y:1250; w:  50; h: 125),
+       (x: 1700; y:1150; w:  50; h: 125),
+       (x: 1850; y: 500; w:  75; h: 125),
+       (x: 1950; y: 550; w:  75; h: 175),
+       (x: 2250; y:1150; w:  50; h: 125),
+       (x: 2350; y:1250; w:  50; h: 125),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 1750; y:2010; w:  26; h:  26),
+       (x: 1900; y:1870; w:  75; h:  75),
+       (x: 2050; y:1870; w:  75; h:  75),
+       (x: 2175; y:2010; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 2500; y:1700; w:  26; h:  26),
+       (x: 2575; y:1500; w:  35; h:  75),
+       (x: 2650; y:1500; w:  35; h:  75),
+       (x: 2700; y:1690; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 2000; y: 125; w:  26; h:  26),
+       (x: 2050; y:  50; w:  50; h:  50),
+       (x: 2100; y:  50; w:  50; h:  50),
+       (x: 2150; y: 150; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 2600; y: 250; w:  50; h: 125),
+       (x: 2750; y: 400; w:  75; h:  75),
+       (x: 2900; y: 525; w:  75; h:  75),
+       (x: 3150; y: 550; w:  75; h: 125),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 2800; y:1150; w:  26; h:  26),
+       (x: 2840; y: 950; w:  50; h:  50),
+       (x: 2880; y: 950; w:  50; h:  50),
+       (x: 2900; y:1150; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 3075; y:1985; w:  26; h:  26),
+       (x: 3325; y:1700; w:  75; h: 125),
+       (x: 3475; y:1700; w:  75; h: 125),
+       (x: 3625; y:1985; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 3200; y:1450; w:  26; h:  26),
+       (x: 3240; y:1350; w:  50; h:  50),
+       (x: 3280; y:1350; w:  50; h:  50),
+       (x: 3300; y:1450; w:  26; h:  26),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 3500; y:1050; w:  50; h:  75),
+       (x: 3650; y: 600; w:  75; h: 125),
+       (x: 3800; y: 600; w:  75; h: 125),
+       (x: 3900; y:1000; w:  50; h:  75),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x: 3800; y: 200; w:  50; h:  75),
+       (x: 3875; y: 100; w:  75; h:  75),
+       (x: 3925; y:  50; w:  75; h:  50),
+       (x: 4050; y: 125; w:  50; h:  75),
+       (x: NTPX; y:   0; w:   1; h:   1),
+       (x:   95; y:2548; w:  26; h:  26),
+       (x:  100; y:2323; w:  50; h: 125),
+       (x:  325; y:2323; w:  50; h: 125),
+       (x:  330; y:2548; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x:  725; y:2173; w:  26; h:  26),
+       (x:  725; y:2073; w:  30; h:  50),
+       (x:  825; y:2083; w:  30; h:  35),
+       (x:  825; y:2183; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 1150; y:2598; w:  50; h:  75),
+       (x: 1250; y:2348; w:  50; h:  75),
+       (x: 1350; y:2348; w:  50; h:  75),
+       (x: 1400; y:2623; w:  50; h:  75),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x:  525; y:3098; w:  75; h:  75),
+       (x:  700; y:2848; w: 125; h: 175),
+       (x:  950; y:2948; w: 125; h: 175),
+       (x: 1100; y:3148; w:  75; h:  75),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x:  175; y:3548; w:  26; h:  26),
+       (x:  210; y:3448; w:  30; h:  50),
+       (x:  240; y:3448; w:  30; h:  50),
+       (x:  275; y:3558; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x:  450; y:3848; w: 125; h: 125),
+       (x:  600; y:3798; w: 125; h: 125),
+       (x:  750; y:3798; w: 125; h: 125),
+       (x:  950; y:3898; w: 125; h: 125),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 1075; y:3498; w:  26; h:  26),
+       (x: 1110; y:3348; w:  30; h:  50),
+       (x: 1140; y:3348; w:  30; h:  50),
+       (x: 1175; y:3478; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 1600; y:3298; w:  50; h: 125),
+       (x: 1700; y:3198; w:  50; h: 125),
+       (x: 1850; y:2548; w:  75; h: 125),
+       (x: 1950; y:2598; w:  75; h: 175),
+       (x: 2250; y:3198; w:  50; h: 125),
+       (x: 2350; y:3298; w:  50; h: 125),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 1750; y:4058; w:  26; h:  26),
+       (x: 1900; y:3918; w:  75; h:  75),
+       (x: 2050; y:3918; w:  75; h:  75),
+       (x: 2175; y:4058; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 2500; y:3748; w:  26; h:  26),
+       (x: 2575; y:3548; w:  35; h:  75),
+       (x: 2650; y:3548; w:  35; h:  75),
+       (x: 2700; y:3738; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 2000; y:2173; w:  26; h:  26),
+       (x: 2050; y:2098; w:  50; h:  50),
+       (x: 2100; y:2098; w:  50; h:  50),
+       (x: 2150; y:2198; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 2600; y:2298; w:  50; h: 125),
+       (x: 2750; y:2448; w:  75; h:  75),
+       (x: 2900; y:2573; w:  75; h:  75),
+       (x: 3150; y:2598; w:  75; h: 125),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 2800; y:3198; w:  26; h:  26),
+       (x: 2840; y:2998; w:  50; h:  50),
+       (x: 2880; y:2998; w:  50; h:  50),
+       (x: 2900; y:3198; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 3075; y:4033; w:  26; h:  26),
+       (x: 3325; y:3748; w:  75; h: 125),
+       (x: 3475; y:3748; w:  75; h: 125),
+       (x: 3625; y:4033; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 3200; y:3498; w:  26; h:  26),
+       (x: 3240; y:3398; w:  50; h:  50),
+       (x: 3280; y:3398; w:  50; h:  50),
+       (x: 3300; y:3498; w:  26; h:  26),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 3500; y:3098; w:  50; h:  75),
+       (x: 3650; y:2648; w:  75; h: 125),
+       (x: 3800; y:2648; w:  75; h: 125),
+       (x: 3900; y:3048; w:  50; h:  75),
+       (x: NTPX; y:2048; w:   1; h:   1),
+       (x: 3800; y:2248; w:  50; h:  75),
+       (x: 3875; y:2148; w:  75; h:  75),
+       (x: 3925; y:2098; w:  75; h:  50),
+       (x: 4050; y:2173; w:  50; h:  75),
+       (x: NTPX; y:2048; w:   1; h:   1)
+      );
+      Template43FPoints: array[0..0] of TPoint =
+      (
+       (X: 4095; Y:    0)
+      );
 
 ////////////////////////////////////////////////////////////////////////
-var EdgeTemplates: array[0..42] of TEdgeTemplate =
+var EdgeTemplates: array[0..43] of TEdgeTemplate =
       (
        (BasePoints: @Template0Points;
         BasePointsCount: Succ(High(Template0Points));
@@ -2030,8 +2212,8 @@
         BasePointsCount: Succ(High(Template41Points));
         FillPoints: @Template41FPoints;
         FillPointsCount: Succ(High(Template41FPoints));
-        BezierizeCount: 3;
-        RandPassesCount: 5;
+        BezierizeCount: 2;
+        RandPassesCount: 9;
         TemplateHeight: 2048; TemplateWidth: 4096;
         canMirror: true; canFlip: true; isNegative: false; canInvert: false;
         hasGirders: true;
@@ -2047,19 +2229,30 @@
         canMirror: true; canFlip: false; isNegative: false; canInvert: false;
         hasGirders: false;
         MaxHedgeHogs: 8;
+       ),
+       (BasePoints: @Template43Points;
+        BasePointsCount: Succ(High(Template43Points));
+        FillPoints: @Template43FPoints;
+        FillPointsCount: Succ(High(Template43FPoints));
+        BezierizeCount: 2;
+        RandPassesCount: 9;
+        TemplateHeight: 4096; TemplateWidth: 4096;
+        canMirror: true; canFlip: true; isNegative: false; canInvert: false;
+        hasGirders: true;
+        MaxHedgeHogs: 48;
        )
       );
 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 LargeTemplates: array[0..19] of Longword =
+const LargeTemplates: array[0..20] of Longword =
       (
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
-        28, 29, 30, 31, 32, 33, 34, 35, 37, 38
+        28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 43
       );
 const CavernTemplates: array[0..4] of Longword = (36, 2, 3, 21, 29);
 //const WackyTemplates: array[0..4] of Longword = (37, 38, 39, 40, 41);
-const WackyTemplates: array[0..2] of Longword = (37, 38, 41);
+const WackyTemplates: array[0..3] of Longword = (37, 38, 41, 43);
 
 implementation
 
--- a/hedgewars/uMisc.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uMisc.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -28,7 +28,7 @@
 
 procedure movecursor(dx, dy: LongInt);
 function  doSurfaceConversion(tmpsurf: PSDL_Surface): PSDL_Surface;
-function  MakeScreenshot(filename: shortstring): boolean;
+function  MakeScreenshot(filename: shortstring; k: LongInt): boolean;
 function  GetTeamStatString(p: PTeam): shortstring;
 {$IFDEF SDL13}
 function  SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline;
@@ -186,19 +186,48 @@
 
 {$ENDIF} // no PNG_SCREENSHOTS
 
+{$IFDEF USE_VIDEO_RECORDING}
+// make image k times smaller (useful for saving thumbnails)
+procedure ReduceImage(img: PByte; width, height, k: LongInt);
+var i, j, i0, j0, w, h, r, g, b: LongInt;
+begin
+    w:= width  div k;
+    h:= height div k;
+
+    // rescale inplace
+    if k <> 1 then
+    begin
+        for i:= 0 to h-1 do
+            for j:= 0 to w-1 do
+            begin
+                r:= 0;
+                g:= 0;
+                b:= 0;
+                for i0:= 0 to k-1 do
+                    for j0:= 0 to k-1 do
+                    begin
+                        r+= img[4*(width*(i*k+i0) + j*k+j0)+0];
+                        g+= img[4*(width*(i*k+i0) + j*k+j0)+1];
+                        b+= img[4*(width*(i*k+i0) + j*k+j0)+2];
+                    end;
+                img[4*(w*i + j)+0]:= r div (k*k);
+                img[4*(w*i + j)+1]:= g div (k*k);
+                img[4*(w*i + j)+2]:= b div (k*k);
+                img[4*(w*i + j)+3]:= 255;
+            end;
+    end;
+end;
+{$ENDIF}
+
 // captures and saves the screen. returns true on success.
-function MakeScreenshot(filename: shortstring): Boolean;
+// saved image will be k times smaller than original (useful for saving thumbnails).
+function MakeScreenshot(filename: shortstring; k: LongInt): Boolean;
 var p: Pointer;
     size: QWord;
     image: PScreenshot;
     format: GLenum;
     ext: string[4];
 begin
-// flash
-ScreenFade:= sfFromWhite;
-ScreenFadeValue:= sfMax;
-ScreenFadeSpeed:= 5;
-
 {$IFDEF PNG_SCREENSHOTS}
 format:= GL_RGBA;
 ext:= '.png';
@@ -218,14 +247,18 @@
     exit;
 end;
 
-// read pixel from the front buffer
+// read pixels from the front buffer
 glReadPixels(0, 0, cScreenWidth, cScreenHeight, format, GL_UNSIGNED_BYTE, p);
 
+{$IFDEF USE_VIDEO_RECORDING}
+ReduceImage(p, cScreenWidth, cScreenHeight, k);
+{$ENDIF}
+
 // allocate and fill structure that will be passed to new thread
 New(image); // will be disposed in SaveScreenshot()
-image^.filename:= UserPathPrefix + '/Screenshots/' + filename + ext;
-image^.width:= cScreenWidth;
-image^.height:= cScreenHeight;
+image^.filename:= UserPathPrefix + filename + ext;
+image^.width:= cScreenWidth div k;
+image^.height:= cScreenHeight div k;
 image^.size:= size;
 image^.buffer:= p;
 
--- a/hedgewars/uMobile.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uMobile.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -28,7 +28,7 @@
 interface
 
 function  isPhone: Boolean; inline;
-function  getScreenDPI: Single; inline;
+function  getScreenDPI: Double; inline;
 procedure performRumble; inline;
 
 procedure GameLoading; inline;
@@ -36,7 +36,7 @@
 procedure SaveLoadingEnded; inline;
 
 implementation
-uses uVariables, uConsole;
+uses uVariables, uConsole, SDLh;
 
 // add here any external call that you need
 {$IFDEF IPHONEOS}
@@ -48,10 +48,6 @@
 procedure AudioServicesPlaySystemSound(num: LongInt); cdecl; external;
 {$ENDIF}
 
-{$IFDEF ANDROID}
-function Android_JNI_getDensity(): Single; cdecl; external;
-{$ENDIF}
-
 // this function is just to determine whether we are running on a limited screen device
 function isPhone: Boolean; inline;
 begin
@@ -66,10 +62,11 @@
 {$ENDIF}
 end;
 
-function getScreenDPI: Single; inline;
+function getScreenDPI: Double; inline;
 begin
 {$IFDEF ANDROID}
-    getScreenDPI:= Android_JNI_getDensity();
+//    getScreenDPI:= Android_JNI_getDensity();
+    getScreenDPI:= 1;
 {$ELSE}
     getScreenDPI:= 1;
 {$ENDIF}
--- a/hedgewars/uRandom.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uRandom.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -30,9 +30,6 @@
 interface
 uses uFloat;
 
-procedure initModule;
-procedure freeModule;
-
 procedure SetRandomSeed(Seed: shortstring); // Sets the seed that should be used for generating pseudo-random values.
 function  GetRandomf: hwFloat; overload; // Returns a pseudo-random hwFloat.
 function  GetRandom(m: LongWord): LongWord; overload; inline; // Returns a positive pseudo-random integer smaller than m.
@@ -99,15 +96,4 @@
 rndSign:= num
 end;
 
-procedure initModule;
-begin
-    n:= 54;
-    FillChar(cirbuf, 64*sizeof(Longword), 0);
-end;
-
-procedure freeModule;
-begin
-
-end;
-
 end.
--- a/hedgewars/uRenderUtils.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uRenderUtils.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -92,7 +92,7 @@
     textRect.y:= Y;
     textRect.w:= w;
     textRect.h:= h;
-    DrawRoundRect(@finalRect, cWhiteColor, endian(cNearBlackColorChannels.value), Surface, true);
+    DrawRoundRect(@finalRect, cWhiteColor, cNearBlackColor, Surface, true);
     clr.r:= (Color shr 16) and $FF;
     clr.g:= (Color shr 8) and $FF;
     clr.b:= Color and $FF;
--- a/hedgewars/uScript.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uScript.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -413,7 +413,7 @@
         begin
         gear:= GearByUID(lua_tointeger(L, 1));
         if gear <> nil then
-            DeleteGear(gear);
+            gear^.Message:= gear^.Message or gmDelete;
         end;
     lc_deletegear:= 0
 end;
@@ -745,7 +745,7 @@
             TryDo(texsurf <> nil, errmsgCreateSurface, true);
             TryDo(SDL_SetColorKey(texsurf, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
 
-            DrawRoundRect(@r, cWhiteColor, cNearBlackColorChannels.value, texsurf, true);
+            DrawRoundRect(@r, cWhiteColor, cNearBlackColor, texsurf, true);
             rr:= r;
             inc(rr.x, 2); dec(rr.w, 4); inc(rr.y, 2); dec(rr.h, 4);
             DrawRoundRect(@rr, clan^.Color, clan^.Color, texsurf, false);
@@ -981,10 +981,13 @@
         if (gear <> nil) and (gear^.Kind = gtHedgehog) and (gear^.Hedgehog <> nil) and (CurrentHedgehog <> nil) then
             begin
             prevgear := CurrentHedgehog^.Gear;
-            prevgear^.Active := false;
-            prevgear^.State:= prevgear^.State and not gstHHDriven;
-            prevgear^.Z := cHHZ;
-            prevgear^.Message:= prevgear^.Message or gmRemoveFromList or gmAddToList;
+            if prevgear <> nil then
+                begin
+                prevgear^.Active := false;
+                prevgear^.State:= prevgear^.State and (not gstHHDriven);
+                prevgear^.Z := cHHZ;
+                prevgear^.Message:= prevgear^.Message or gmRemoveFromList or gmAddToList;
+                end;
             
             SwitchCurrentHedgehog(gear^.Hedgehog);
             CurrentTeam:= CurrentHedgehog^.Team;
@@ -1808,6 +1811,7 @@
 ScriptSetInteger('GameFlags', GameFlags);
 ScriptSetString('Seed', cSeed);
 ScriptSetInteger('TemplateFilter', cTemplateFilter);
+ScriptSetInteger('TemplateNumber', LuaTemplateNumber);
 ScriptSetInteger('MapGen', cMapGen);
 ScriptSetInteger('ScreenHeight', cScreenHeight);
 ScriptSetInteger('ScreenWidth', cScreenWidth);
@@ -1836,6 +1840,7 @@
 // pop game variables
 ParseCommand('seed ' + ScriptGetString('Seed'), true);
 cTemplateFilter  := ScriptGetInteger('TemplateFilter');
+LuaTemplateNumber:= ScriptGetInteger('TemplateNumber');
 cMapGen          := ScriptGetInteger('MapGen');
 GameFlags        := ScriptGetInteger('GameFlags');
 cHedgehogTurnTime:= ScriptGetInteger('TurnTime');
--- a/hedgewars/uSound.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uSound.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -587,11 +587,8 @@
     RegisterVariable('mute'     , @chMute     , true );
 
     MusicFN:='';
-    isMusicEnabled:= true;
-    isSoundEnabled:= true;
     isAudioMuted:= false;
     isSEBackup:= isSoundEnabled;
-    cInitVolume:= 100;
     Volume:= 0;
     defVoicepack:= AskForVoicepack('Default');
 
@@ -615,6 +612,11 @@
 begin
     if isSoundEnabled then
         ReleaseSound(true);
+    // koda still needs to fix this properly.  when he rearranged things, he made these variables get
+    // reset after argparsers picks them up
+    isMusicEnabled:= true;
+    isSoundEnabled:= true;
+    cInitVolume:= 100;
 end;
 
 end.
--- a/hedgewars/uStore.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uStore.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -40,13 +40,17 @@
 procedure ShowWeaponTooltip(x, y: LongInt);
 procedure FreeWeaponTooltip;
 procedure MakeCrossHairs;
+{$IFDEF USE_VIDEO_RECORDING}
+procedure InitOffscreenOpenGL;
+{$ENDIF}
 
 procedure WarpMouse(x, y: Word); inline;
 procedure SwapBuffers; inline;
 
 implementation
 uses uMisc, uConsole, uMobile, uVariables, uUtils, uTextures, uRender, uRenderUtils, uCommands,
-     uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF};
+     uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF}
+     {$IF NOT DEFINED(SDL13) AND DEFINED(USE_VIDEO_RECORDING)}, glut {$ENDIF};
 
 //type TGPUVendor = (gvUnknown, gvNVIDIA, gvATI, gvIntel, gvApple);
 
@@ -131,10 +135,11 @@
 
 procedure WriteNames(Font: THWFont);
 var t: LongInt;
-    i: LongInt;
+    i, maxLevel: LongInt;
     r, rr: TSDL_Rect;
     drY: LongInt;
     texsurf, flagsurf, iconsurf: PSDL_Surface;
+    foundBot: boolean;
 begin
 r.x:= 0;
 r.y:= 0;
@@ -151,7 +156,7 @@
         TryDo(texsurf <> nil, errmsgCreateSurface, true);
         TryDo(SDL_SetColorKey(texsurf, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
 
-        DrawRoundRect(@r, cWhiteColor, cNearBlackColorChannels.value, texsurf, true);
+        DrawRoundRect(@r, cWhiteColor, cNearBlackColor, texsurf, true);
         rr:= r;
         inc(rr.x, 2); dec(rr.w, 4); inc(rr.y, 2); dec(rr.h, 4);
         DrawRoundRect(@rr, Clan^.Color, Clan^.Color, texsurf, false);
@@ -172,11 +177,28 @@
         DrawRoundRect(@r, cWhiteColor, cNearBlackColor, texsurf, true);
 
         // overwrite flag for cpu teams and keep players from using it
-        if (Hedgehogs[0].Gear <> nil) and (Hedgehogs[0].BotLevel > 0) then
-            if Flag = 'hedgewars' then
-                Flag:= 'cpu'
-        else if Flag = 'cpu' then
-            Flag:= 'hedgewars';
+        foundBot:= false;
+        maxLevel:= -1;
+        for i:= 0 to cMaxHHIndex do
+            with Hedgehogs[i] do
+                if (Gear <> nil) and (BotLevel > 0) then
+                    begin
+                    foundBot:= true;
+                    // initially was going to do the highest botlevel of the team, but for now, just apply if entire team has same bot level
+                    if maxLevel = -1 then maxLevel:= BotLevel
+                    else if (maxLevel > 0) and (maxLevel <> BotLevel) then maxLevel:= 0; 
+                    //if (maxLevel > 0) and (BotLevel < maxLevel) then maxLevel:= BotLevel
+                    end
+                else if Gear <> nil then  maxLevel:= 0;
+
+        if foundBot then
+            begin
+            // disabled the plain flag - I think it looks ok even w/ full bars obscuring CPU
+            //if (maxLevel > 0) and (maxLevel < 3) then Flag:= 'cpu_plain' else 
+            Flag:= 'cpu'
+            end
+        else if (Flag = 'cpu') or (Flag = 'cpu_plain') then
+                Flag:= 'hedgewars';
 
         flagsurf:= LoadImage(UserPathz[ptFlags] + '/' + Flag, ifNone);
         if flagsurf = nil then
@@ -186,16 +208,27 @@
         if flagsurf = nil then
             flagsurf:= LoadImage(Pathz[ptFlags] + '/hedgewars', ifNone);
         TryDo(flagsurf <> nil, 'Failed to load flag "' + Flag + '" as well as the default flag', true);
+
+        case maxLevel of
+            1: copyToXY(SpritesData[sprBotlevels].Surface, flagsurf, 0, 0); 
+            2: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 5, 2, 17, 13, 5, 2); 
+            3: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 9, 5, 13, 10, 9, 5); 
+            4: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 13, 9, 9, 6, 13, 9); 
+            5: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 17, 11, 5, 4, 17, 11)
+            end;
+
         copyToXY(flagsurf, texsurf, 2, 2);
         SDL_FreeSurface(flagsurf);
         flagsurf:= nil;
 
+
         // restore black border pixels inside the flag
         PLongwordArray(texsurf^.pixels)^[32 * 2 +  2]:= cNearBlackColor;
         PLongwordArray(texsurf^.pixels)^[32 * 2 + 23]:= cNearBlackColor;
         PLongwordArray(texsurf^.pixels)^[32 * 16 +  2]:= cNearBlackColor;
         PLongwordArray(texsurf^.pixels)^[32 * 16 + 23]:= cNearBlackColor;
 
+
         FlagTex:= Surface2Tex(texsurf, false);
         SDL_FreeSurface(texsurf);
         texsurf:= nil;
@@ -204,7 +237,7 @@
 
         dec(drY, r.h + 2);
         DrawHealthY:= drY;
-        for i:= 0 to 7 do
+        for i:= 0 to cMaxHHIndex do
             with Hedgehogs[i] do
                 if Gear <> nil then
                     begin
@@ -294,7 +327,6 @@
             WriteLnToConsole(msgOK)
             end;
 
-WriteNames(fnt16);
 MakeCrossHairs;
 LoadGraves;
 if not reload then
@@ -391,6 +423,8 @@
                 Surface:= nil
         end;
 
+WriteNames(fnt16);
+
 if not reload then
     AddProgress;
 
@@ -438,6 +472,31 @@
 IMG_Quit();
 end;
 
+{$IF NOT DEFINED(S3D_DISABLED) OR DEFINED(USE_VIDEO_RECORDING)}
+procedure CreateFramebuffer(var frame, depth, tex: GLuint);
+begin
+    glGenFramebuffersEXT(1, @frame);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frame);
+    glGenRenderbuffersEXT(1, @depth);
+    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth);
+    glGenTextures(1, @tex);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);
+end;
+
+procedure DeleteFramebuffer(var frame, depth, tex: GLuint);
+begin
+    glDeleteTextures(1, @tex);
+    glDeleteRenderbuffersEXT(1, @depth);
+    glDeleteFramebuffersEXT(1, @frame);
+end;
+{$ENDIF}
+
 procedure StoreRelease(reload: boolean);
 var ii: TSprite;
     ai: TAmmoType;
@@ -511,15 +570,15 @@
                 end;
             end;
         end;
+{$IFDEF USE_VIDEO_RECORDING}
+    if defaultFrame <> 0 then
+        DeleteFramebuffer(defaultFrame, depthv, texv);
+{$ENDIF}
 {$IFNDEF S3D_DISABLED}
     if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then
         begin
-        glDeleteTextures(1, @texl);
-        glDeleteRenderbuffersEXT(1, @depthl);
-        glDeleteFramebuffersEXT(1, @framel);
-        glDeleteTextures(1, @texr);
-        glDeleteRenderbuffersEXT(1, @depthr);
-        glDeleteFramebuffersEXT(1, @framer)
+        DeleteFramebuffer(framel, depthl, texl);
+        DeleteFramebuffer(framer, depthr, texr);
         end
 {$ENDIF}
 end;
@@ -628,6 +687,7 @@
 procedure SetupOpenGL;
 //var vendor: shortstring = '';
 var buf: array[byte] of char;
+    AuxBufNum: LongInt;
 begin
     buf[0]:= char(0); // avoid compiler hint
     AddFileLog('Setting up OpenGL (using driver: ' + shortstring(SDL_VideoDriverName(buf, sizeof(buf))) + ')');
@@ -673,14 +733,43 @@
 {$ENDIF}
 //SupportNPOTT:= glLoadExtension('GL_ARB_texture_non_power_of_two');
 *)
+    glGetIntegerv(GL_AUX_BUFFERS, @AuxBufNum);
 
     // everyone love debugging
     AddFileLog('OpenGL-- Renderer: ' + shortstring(pchar(glGetString(GL_RENDERER))));
     AddFileLog('  |----- Vendor: ' + shortstring(pchar(glGetString(GL_VENDOR))));
     AddFileLog('  |----- Version: ' + shortstring(pchar(glGetString(GL_VERSION))));
     AddFileLog('  |----- Texture Size: ' + inttostr(MaxTextureSize));
-    AddFileLog('  \----- Extensions: ' + shortstring(pchar(glGetString(GL_EXTENSIONS))));
-    //TODO: don't have the Extensions line trimmed but slipt it into multiple lines
+    AddFileLog('  |----- Number of auxilary buffers: ' + inttostr(AuxBufNum));
+    AddFileLog('  \----- Extensions: ');
+    AddFileLogRaw(glGetString(GL_EXTENSIONS));
+    AddFileLog('');
+    //TODO: slipt Extensions line into multiple lines
+
+    defaultFrame:= 0;
+{$IFDEF USE_VIDEO_RECORDING}
+    if GameType = gmtRecord then
+    begin
+        if glLoadExtension('GL_EXT_framebuffer_object') then
+        begin
+            CreateFramebuffer(defaultFrame, depthv, texv);
+            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame);
+            AddFileLog('Using framebuffer for video recording.');
+        end
+        else if AuxBufNum > 0 then
+        begin
+            glDrawBuffer(GL_AUX0);
+            glReadBuffer(GL_AUX0);
+            AddFileLog('Using auxilary buffer for video recording.');
+        end
+        else
+        begin
+            glDrawBuffer(GL_BACK);
+            glReadBuffer(GL_BACK);
+            AddFileLog('Warning: off-screen rendering is not supported; using back buffer but it may not work.');
+        end;
+    end;
+{$ENDIF}
 
 {$IFNDEF S3D_DISABLED}
     if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then
@@ -688,36 +777,11 @@
         // prepare left and right frame buffers and associated textures
         if glLoadExtension('GL_EXT_framebuffer_object') then
             begin
-            // left
-            glGenFramebuffersEXT(1, @framel);
-            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framel);
-            glGenRenderbuffersEXT(1, @depthl);
-            glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthl);
-            glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
-            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthl);
-            glGenTextures(1, @texl);
-            glBindTexture(GL_TEXTURE_2D, texl);
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texl, 0);
-
-            // right
-            glGenFramebuffersEXT(1, @framer);
-            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framer);
-            glGenRenderbuffersEXT(1, @depthr);
-            glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthr);
-            glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
-            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthr);
-            glGenTextures(1, @texr);
-            glBindTexture(GL_TEXTURE_2D, texr);
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,  cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texr, 0);
+            CreateFramebuffer(framel, depthl, texl);
+            CreateFramebuffer(framer, depthr, texr);
 
             // reset
-            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)
+            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame)
             end
         else
             cStereoMode:= smNone;
@@ -991,6 +1055,34 @@
 WeaponTooltipTex:= nil
 end;
 
+{$IFDEF USE_VIDEO_RECORDING}
+{$IFDEF SDL13}
+procedure InitOffscreenOpenGL;
+begin
+    // create hidden window
+    SDLwindow:= SDL_CreateWindow('hedgewars (you don''t see this)',
+                                 SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK,
+                                 cScreenWidth, cScreenHeight,
+                                 SDL_WINDOW_HIDDEN or SDL_WINDOW_OPENGL);
+    SDLTry(SDLwindow <> nil, true);
+    SetupOpenGL();
+end;
+{$ELSE}
+procedure InitOffscreenOpenGL;
+var ArgCount: LongInt;
+    PrgName: pchar;
+begin
+    ArgCount:= 1;
+    PrgName:= 'hwengine';
+    glutInit(@ArgCount, @PrgName);
+    glutInitWindowSize(cScreenWidth, cScreenHeight);
+    glutCreateWindow('hedgewars (you don''t see this)'); // we don't need a window, but if this function is not called then OpenGL will not be initialized
+    glutHideWindow();
+    SetupOpenGL();
+end;
+{$ENDIF} // SDL13
+{$ENDIF} // USE_VIDEO_RECORDING
+
 procedure chFullScr(var s: shortstring);
 var flags: Longword = 0;
     reinit: boolean = false;
@@ -1171,6 +1263,8 @@
 
 procedure SwapBuffers; inline;
 begin
+    if GameType = gmtRecord then
+        exit;
 {$IFDEF SDL13}
     SDL_GL_SwapWindow(SDLwindow);
 {$ELSE}
--- a/hedgewars/uTeams.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uTeams.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -473,7 +473,7 @@
                 begin
                 Gear^.Invulnerable:= false;
                 Gear^.Damage:= Gear^.Health;
-                Gear^.State:= (Gear^.State or gstHHGone) and not gstHHDriven
+                Gear^.State:= (Gear^.State or gstHHGone) and (not gstHHDriven)
                 end
             end
 end;
@@ -531,7 +531,7 @@
     AddTeam(Color);
     CurrentTeam^.TeamName:= ts;
     CurrentTeam^.PlayerHash:= s;
-    if GameType in [gmtDemo, gmtSave] then
+    if GameType in [gmtDemo, gmtSave, gmtRecord] then
         CurrentTeam^.ExtDriven:= true;
 
     CurrentTeam^.voicepack:= AskForVoicepack('Default')
--- a/hedgewars/uTypes.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uTypes.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -39,7 +39,7 @@
     TGameState = (gsLandGen, gsStart, gsGame, gsChat, gsConfirm, gsExit, gsSuspend);
 
     // Game types that help determining what the engine is actually supposed to do
-    TGameType = (gmtLocal, gmtDemo, gmtNet, gmtSave, gmtLandPreview, gmtSyntax);
+    TGameType = (gmtLocal, gmtDemo, gmtNet, gmtSave, gmtLandPreview, gmtSyntax, gmtRecord);
 
     // Different files are stored in different folders, this enumeration is used to tell which folder to use
     TPathType = (ptNone, ptData, ptGraphics, ptThemes, ptCurrTheme, ptTeams, ptMaps,
@@ -86,7 +86,7 @@
             sprHandResurrector, sprCross, sprAirDrill, sprNapalmBomb,
             sprBulletHit, sprSnowball, sprHandSnowball, sprSnow,
             sprSDFlake, sprSDWater, sprSDCloud, sprSDSplash, sprSDDroplet, sprTardis,
-            sprSlider
+            sprSlider, sprBotlevels
             );
 
     // Gears that interact with other Gears and/or Land
@@ -235,6 +235,7 @@
             Kind: TGearType;
             Pos: Longword;
             doStep: TGearStepProcedure;
+            stepFreq: Longword;
             Radius: LongInt;
             Angle, Power : Longword;
             DirAngle: real;
--- a/hedgewars/uUtils.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uUtils.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -43,7 +43,7 @@
 function  StrToInt(s: shortstring): LongInt;
 function  FloatToStr(n: hwFloat): shortstring;
 
-function  DxDy2Angle(const _dY, _dX: hwFloat): GLfloat;
+function  DxDy2Angle(const _dY, _dX: hwFloat): GLfloat; inline;
 function  DxDy2Angle32(const _dY, _dX: hwFloat): LongInt;
 function  DxDy2AttackAngle(const _dY, _dX: hwFloat): LongInt;
 function  DxDy2AttackAnglef(const _dY, _dX: extended): LongInt;
@@ -61,6 +61,7 @@
 function  CheckCJKFont(s: ansistring; font: THWFont): THWFont;
 
 procedure AddFileLog(s: shortstring);
+procedure AddFileLogRaw(s: pchar); cdecl;
 
 function  CheckNoTeamOrHH: boolean; inline;
 
@@ -81,6 +82,9 @@
 
 {$IFDEF DEBUGFILE}
 var f: textfile;
+{$IFDEF USE_VIDEO_RECORDING}
+    logMutex: TRTLCriticalSection; // mutex for debug file
+{$ENDIF}
 {$ENDIF}
 var CharArray: array[byte] of Char;
 
@@ -182,15 +186,11 @@
 end;
 
 
-function DxDy2Angle(const _dY, _dX: hwFloat): GLfloat;
+function DxDy2Angle(const _dY, _dX: hwFloat): GLfloat; inline;
 var dY, dX: Extended;
 begin
-dY:= _dY.QWordValue / $100000000;
-if _dY.isNegative then
-    dY:= - dY;
-dX:= _dX.QWordValue / $100000000;
-if _dX.isNegative then
-    dX:= - dX;
+dY:= hwFloat2Float(_dY);
+dX:= hwFloat2Float(_dX);
 DxDy2Angle:= arctan2(dY, dX) * 180 / pi
 end;
 
@@ -198,12 +198,8 @@
 const _16divPI: Extended = 16/pi;
 var dY, dX: Extended;
 begin
-dY:= _dY.QWordValue / $100000000;
-if _dY.isNegative then
-    dY:= - dY;
-dX:= _dX.QWordValue / $100000000;
-if _dX.isNegative then
-    dX:= - dX;
+dY:= hwFloat2Float(_dY);
+dX:= hwFloat2Float(_dX);
 DxDy2Angle32:= trunc(arctan2(dY, dX) * _16divPI) and $1f
 end;
 
@@ -211,12 +207,8 @@
 const MaxAngleDivPI: Extended = cMaxAngle/pi;
 var dY, dX: Extended;
 begin
-dY:= _dY.QWordValue / $100000000;
-if _dY.isNegative then
-    dY:= - dY;
-dX:= _dX.QWordValue / $100000000;
-if _dX.isNegative then
-    dX:= - dX;
+dY:= hwFloat2Float(_dY);
+dX:= hwFloat2Float(_dX);
 DxDy2AttackAngle:= trunc(arctan2(dY, dX) * MaxAngleDivPI)
 end;
 
@@ -303,11 +295,31 @@
 begin
 s:= s;
 {$IFDEF DEBUGFILE}
+{$IFDEF USE_VIDEO_RECORDING}
+EnterCriticalSection(logMutex);
+{$ENDIF}
 writeln(f, inttostr(GameTicks)  + ': ' + s);
-flush(f)
+flush(f);
+{$IFDEF USE_VIDEO_RECORDING}
+LeaveCriticalSection(logMutex);
+{$ENDIF}
 {$ENDIF}
 end;
 
+procedure AddFileLogRaw(s: pchar); cdecl;
+begin
+s:= s;
+{$IFDEF DEBUGFILE}
+{$IFDEF USE_VIDEO_RECORDING}
+EnterCriticalSection(logMutex);
+{$ENDIF}
+write(f, s);
+flush(f);
+{$IFDEF USE_VIDEO_RECORDING}
+LeaveCriticalSection(logMutex);
+{$ENDIF}
+{$ENDIF}
+end;
 
 function CheckCJKFont(s: ansistring; font: THWFont): THWFont;
 var l, i : LongInt;
@@ -397,9 +409,17 @@
 begin
 {$IFDEF DEBUGFILE}
     if isGame then
-        logfileBase:= 'game'
+    begin
+        if GameType = gmtRecord then
+            logfileBase:= 'rec'
+        else
+            logfileBase:= 'game';
+    end
     else
         logfileBase:= 'preview';
+{$IFDEF USE_VIDEO_RECORDING}
+    InitCriticalSection(logMutex);
+{$ENDIF}
 {$I-}
 {$IFDEF MOBILE}
     {$IFDEF IPHONEOS} Assign(f,'../Documents/hw-' + logfileBase + '.log'); {$ENDIF}
@@ -436,6 +456,9 @@
     writeln(f, 'halt at ' + inttostr(GameTicks) + ' ticks. TurnTimeLeft = ' + inttostr(TurnTimeLeft));
     flush(f);
     close(f);
+{$IFDEF USE_VIDEO_RECORDING}
+    DoneCriticalSection(logMutex);
+{$ENDIF}
 {$ENDIF}
 end;
 
--- a/hedgewars/uVariables.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uVariables.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -52,6 +52,15 @@
     cReadyDelay     : Longword    = 5000;
     cStereoMode     : TStereoMode = smNone;
     cOnlyStats      : boolean = False;
+{$IFDEF USE_VIDEO_RECORDING}
+    RecPrefix      : shortstring;
+    cAVFormat       : shortstring;
+    cVideoCodec     : shortstring;
+    cVideoFramerateNum : LongInt;
+    cVideoFramerateDen : LongInt;
+    cVideoQuality      : LongInt;
+    cAudioCodec     : shortstring;
+{$ENDIF}
 //////////////////////////
     cMapName        : shortstring = '';
 
@@ -60,8 +69,10 @@
     isPaused        : boolean;
     isInMultiShoot  : boolean;
     isSpeed         : boolean;
+    SpeedStart      : LongWord;
 
     fastUntilLag    : boolean;
+    fastScrolling   : boolean;
     autoCameraOn    : boolean;
 
     CheckSum        : LongWord;
@@ -185,6 +196,8 @@
     hiddenHedgehogs : array [0..cMaxHHs] of PHedgehog;
     hiddenHedgehogsNumber : longint;
 
+    LuaTemplateNumber : LongWord;
+
     VoiceList : array[0..7] of TVoice =  (
                     ( snd: sndNone; voicepack: nil),
                     ( snd: sndNone; voicepack: nil),
@@ -646,7 +659,9 @@
             (FileName:  'TARDIS'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
             Width:  48; Height: 79; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprTardis
             (FileName:  'slider'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 3; Height: 17; imageWidth: 3; imageHeight: 17; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: false) // sprSlider
+            Width: 3; Height: 17; imageWidth: 3; imageHeight: 17; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: false), // sprSlider
+            (FileName:  'botlevels'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
+            Width: 22; Height: 15; imageWidth: 22; imageHeight: 15; saveSurf: true; priority: tpLow; getDimensions: false; getImageDimensions: false) // sprBotlevels
             );
 
 const
@@ -1448,7 +1463,8 @@
             NumberInCase: 1;
             Ammo: (Propz: ammoprop_ForwMsgs or 
                           ammoprop_NoCrosshair or 
-                          ammoprop_DontHold;
+                          ammoprop_DontHold or
+                          ammoprop_Track;
                 Count: 1;
                 NumPerTurn: 0;
                 Timer: 0;
@@ -2446,6 +2462,10 @@
     framel, framer, depthl, depthr: GLuint;
     texl, texr: GLuint;
 
+    // video recorder framebuffer and texture
+    defaultFrame, depthv: GLuint;
+    texv: GLuint;
+
     VisualGearLayers: array[0..6] of PVisualGear;
     lastVisualGearByUID: PVisualGear;
     vobFrameTicks, vobFramesCount, vobCount: Longword;
@@ -2571,7 +2591,7 @@
     cExplosives     := 2;
 
     GameState       := Low(TGameState);
-    GameType        := gmtLocal;
+//    GameType        := gmtLocal;
     zoom            := cDefaultZoomLevel;
     ZoomValue       := cDefaultZoomLevel;
     WeaponTooltipTex:= nil;
@@ -2586,7 +2606,9 @@
     isPaused        := false;
     isInMultiShoot  := false;
     isSpeed         := false;
+    SpeedStart      := 0;
     fastUntilLag    := false;
+    fastScrolling   := false;
     autoCameraOn    := true;
     cScriptName     := '';
     cSeed           := '';
@@ -2621,6 +2643,8 @@
     SDWaterOpacity:= $80;
 
     LuaGoals:= '';
+
+    LuaTemplateNumber:= 0;
 end;
 
 procedure freeModule;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uVideoRec.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,369 @@
+(*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *)
+
+
+{$INCLUDE "options.inc"}
+
+unit uVideoRec;
+
+{$IFNDEF USE_VIDEO_RECORDING}
+interface
+implementation
+end.
+{$ELSE}
+
+{$IFNDEF WIN32}
+    {$LINKLIB ../bin/libavwrapper.a}
+{$ENDIF}
+
+interface
+
+var flagPrerecording: boolean = false;
+
+function BeginVideoRecording: Boolean;
+function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean;
+procedure EncodeFrame;
+procedure StopVideoRecording;
+
+procedure BeginPreRecording;
+procedure StopPreRecording;
+procedure SaveCameraPosition;
+
+procedure freeModule;
+
+implementation
+
+uses uVariables, uUtils, GLunit, SDLh, SysUtils, uIO, uMisc, uTypes;
+
+type TAddFileLogRaw = procedure (s: pchar); cdecl;
+
+procedure AVWrapper_Init(
+              AddLog: TAddFileLogRaw;
+              filename, desc, soundFile, format, vcodec, acodec: PChar;
+              width, height, framerateNum, framerateDen, vquality: LongInt); cdecl; external {$IFDEF WIN32}'libavwrapper.dll'{$ENDIF};
+procedure AVWrapper_Close; cdecl; external {$IFDEF WIN32}'libavwrapper.dll'{$ENDIF};
+procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external {$IFDEF WIN32}'libavwrapper.dll'{$ENDIF};
+
+type TFrame = record
+                  realTicks: LongWord;
+                  gameTicks: LongWord;
+                  CamX, CamY: LongInt;
+                  zoom: single;
+              end;
+
+var YCbCr_Planes: array[0..2] of PByte;
+    RGB_Buffer: PByte;
+    cameraFile: File of TFrame;
+    audioFile: File;
+    numPixels: LongWord;
+    startTime, numFrames, curTime, progress, maxProgress: LongWord;
+    soundFilePath: shortstring;
+    thumbnailSaved : Boolean;
+
+function BeginVideoRecording: Boolean;
+var filename, desc: shortstring;
+begin
+    AddFileLog('BeginVideoRecording');
+
+{$IOCHECKS OFF}
+    // open file with prerecorded camera positions
+    filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin';
+    Assign(cameraFile, filename);
+    Reset(cameraFile);
+    maxProgress:= FileSize(cameraFile);
+    if IOResult <> 0 then
+    begin
+        AddFileLog('Error: Could not read from ' + filename);
+        exit(false);
+    end;
+{$IOCHECKS ON}
+
+    // store some description in output file
+    desc:= '';
+    if UserNick <> '' then
+        desc+= 'Player: ' + UserNick + #10;
+    if recordFileName <> '' then
+        desc+= 'Record: ' + recordFileName + #10;
+    if cMapName <> '' then
+        desc+= 'Map: ' + cMapName + #10;
+    if Theme <> '' then
+        desc+= 'Theme: ' + Theme + #10;
+    desc+= 'prefix[' + RecPrefix + ']prefix';
+    desc+= #0;
+
+    filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + #0;
+    soundFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw' + #0;
+    cAVFormat+= #0;
+    cAudioCodec+= #0;
+    cVideoCodec+= #0;
+    AVWrapper_Init(@AddFileLogRaw, @filename[1], @desc[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1],
+                   cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cVideoQuality);
+
+    numPixels:= cScreenWidth*cScreenHeight;
+    YCbCr_Planes[0]:= GetMem(numPixels);
+    YCbCr_Planes[1]:= GetMem(numPixels div 4);
+    YCbCr_Planes[2]:= GetMem(numPixels div 4);
+
+    if (YCbCr_Planes[0] = nil) or (YCbCr_Planes[1] = nil) or (YCbCr_Planes[2] = nil) then
+    begin
+        AddFileLog('Error: Could not allocate memory for video recording (YCbCr buffer).');
+        exit(false);
+    end;
+
+    RGB_Buffer:= GetMem(4*numPixels);
+    if RGB_Buffer = nil then
+    begin
+        AddFileLog('Error: Could not allocate memory for video recording (RGB buffer).');
+        exit(false);
+    end;
+
+    curTime:= 0;
+    numFrames:= 0;
+    progress:= 0;
+    BeginVideoRecording:= true;
+end;
+
+procedure StopVideoRecording;
+begin
+    AddFileLog('StopVideoRecording');
+    FreeMem(YCbCr_Planes[0], numPixels);
+    FreeMem(YCbCr_Planes[1], numPixels div 4);
+    FreeMem(YCbCr_Planes[2], numPixels div 4);
+    FreeMem(RGB_Buffer, 4*numPixels);
+    Close(cameraFile);
+    AVWrapper_Close();
+    Erase(cameraFile);
+    DeleteFile(soundFilePath);
+    SendIPC(_S'v'); // inform frontend that we finished
+end;
+
+function pixel(x, y, color: LongInt): LongInt;
+begin
+    pixel:= RGB_Buffer[(cScreenHeight-y-1)*cScreenWidth*4 + x*4 + color];
+end;
+
+procedure EncodeFrame;
+var x, y, r, g, b: LongInt;
+    s: shortstring;
+begin
+    // read pixels from OpenGL
+    glReadPixels(0, 0, cScreenWidth, cScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, RGB_Buffer);
+
+    // convert to YCbCr 4:2:0 format
+    // Y
+    for y := 0 to cScreenHeight-1 do
+        for x := 0 to cScreenWidth-1 do
+            YCbCr_Planes[0][y*cScreenWidth + x]:= Byte(16 + ((16828*pixel(x,y,0) + 33038*pixel(x,y,1) + 6416*pixel(x,y,2)) shr 16));
+
+    // Cb and Cr
+    for y := 0 to cScreenHeight div 2 - 1 do
+        for x := 0 to cScreenWidth div 2 - 1 do
+        begin
+            r:= pixel(2*x,2*y,0) + pixel(2*x+1,2*y,0) + pixel(2*x,2*y+1,0) + pixel(2*x+1,2*y+1,0);
+            g:= pixel(2*x,2*y,1) + pixel(2*x+1,2*y,1) + pixel(2*x,2*y+1,1) + pixel(2*x+1,2*y+1,1);
+            b:= pixel(2*x,2*y,2) + pixel(2*x+1,2*y,2) + pixel(2*x,2*y+1,2) + pixel(2*x+1,2*y+1,2);
+            YCbCr_Planes[1][y*(cScreenWidth div 2) + x]:= Byte(128 + ((-2428*r - 4768*g + 7196*b) shr 16));
+            YCbCr_Planes[2][y*(cScreenWidth div 2) + x]:= Byte(128 + (( 7196*r - 6026*g - 1170*b) shr 16));
+        end;
+
+    AVWrapper_WriteFrame(YCbCr_Planes[0], YCbCr_Planes[1], YCbCr_Planes[2]);
+
+    // inform frontend that we have encoded new frame
+    s[0]:= #3;
+    s[1]:= 'p'; // p for progress
+    SDLNet_Write16(progress*10000 div maxProgress, @s[2]);
+    SendIPC(s);
+    inc(numFrames);
+end;
+
+function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean;
+var frame: TFrame;
+begin
+    // we need to skip or duplicate frames to match target framerate
+    while Int64(curTime)*cVideoFramerateNum <= Int64(numFrames)*cVideoFramerateDen*1000 do
+    begin
+    {$IOCHECKS OFF}
+        if eof(cameraFile) then
+            exit(false);
+        BlockRead(cameraFile, frame, 1);
+    {$IOCHECKS ON}
+        curTime:= frame.realTicks;
+        WorldDx:= frame.CamX;
+        WorldDy:= frame.CamY + cScreenHeight div 2;
+        zoom:= frame.zoom*cScreenWidth;
+        ZoomValue:= zoom;
+        inc(progress);
+        newRealTicks:= frame.realTicks;
+        newGameTicks:= frame.gameTicks;
+    end;
+    LoadNextCameraPosition:= true;
+end;
+
+// Callback which records sound.
+// This procedure may be called from different thread.
+procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl;
+begin
+    udata:= udata; // avoid warning
+{$IOCHECKS OFF}
+    BlockWrite(audioFile, stream^, len);
+{$IOCHECKS ON}
+end;
+
+procedure SaveThumbnail;
+var thumbpath: shortstring;
+    k: LongInt;
+begin
+    thumbpath:= '/VideoTemp/' + RecPrefix;
+    AddFileLog('Saving thumbnail ' + thumbpath);
+    k:= max(max(cScreenWidth, cScreenHeight) div 400, 1); // here 400 is minimum size of thumbnail
+    MakeScreenshot(thumbpath, k);
+    thumbnailSaved:= true;
+end;
+
+// copy file (free pascal doesn't have copy file function)
+procedure CopyFile(src, dest: shortstring);
+var inF, outF: file;
+    buffer: array[0..1023] of byte;
+    result: LongInt;
+begin
+{$IOCHECKS OFF}
+    result:= 0; // avoid compiler hint
+
+    Assign(inF, src);
+    Reset(inF, 1);
+    if IOResult <> 0 then
+    begin
+        AddFileLog('Error: Could not read from ' + src);
+        exit;
+    end;
+
+    Assign(outF, dest);
+    Rewrite(outF, 1);
+    if IOResult <> 0 then
+    begin
+        AddFileLog('Error: Could not write to ' + dest);
+        exit;
+    end;
+
+    repeat
+        BlockRead(inF, buffer, 1024, result);
+        BlockWrite(outF, buffer, result);
+    until result < 1024;
+{$IOCHECKS ON}
+end;
+
+procedure BeginPreRecording;
+var format: word;
+    filename: shortstring;
+    frequency, channels: LongInt;
+begin
+    AddFileLog('BeginPreRecording');
+
+    thumbnailSaved:= false;
+    RecPrefix:= 'hw-' + FormatDateTime('YYYY-MM-DD_HH-mm-ss-z', Now());
+
+    // If this video is recorded from demo executed directly (without frontend)
+    // then we need to copy demo so that frontend will be able to find it later.
+    if recordFileName <> '' then
+    begin
+        if GameType <> gmtDemo then // this is save and game demo is not recording, abort
+            exit;
+        CopyFile(recordFileName, UserPathPrefix + '/VideoTemp/' + RecPrefix + '.hwd');
+    end;
+
+    Mix_QuerySpec(@frequency, @format, @channels);
+    AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
+    if format <> $8010 then
+    begin
+        // TODO: support any audio format
+        AddFileLog('Error: Unexpected audio format ' + IntToStr(format));
+        exit;
+    end;
+
+{$IOCHECKS OFF}
+    // create sound file
+    filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw';
+    Assign(audioFile, filename);
+    Rewrite(audioFile, 1);
+    if IOResult <> 0 then
+    begin
+        AddFileLog('Error: Could not write to ' + filename);
+        exit;
+    end;
+
+    // create file with camera positions
+    filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtout';
+    Assign(cameraFile, filename);
+    Rewrite(cameraFile);
+    if IOResult <> 0 then
+    begin
+        AddFileLog('Error: Could not write to ' + filename);
+        exit;
+    end;
+
+    // save audio parameters in sound file
+    BlockWrite(audioFile, frequency, 4);
+    BlockWrite(audioFile, channels, 4);
+{$IOCHECKS ON}
+
+    // register callback for actual audio recording
+    Mix_SetPostMix(@RecordPostMix, nil);
+
+    startTime:= SDL_GetTicks();
+    flagPrerecording:= true;
+end;
+
+procedure StopPreRecording;
+begin
+    AddFileLog('StopPreRecording');
+    flagPrerecording:= false;
+
+    // call SDL_LockAudio because RecordPostMix may be executing right now
+    SDL_LockAudio();
+    Close(audioFile);
+    Close(cameraFile);
+    Mix_SetPostMix(nil, nil);
+    SDL_UnlockAudio();
+
+    if not thumbnailSaved then
+        SaveThumbnail();
+end;
+
+procedure SaveCameraPosition;
+var frame: TFrame;
+begin
+    if (not thumbnailSaved) and (ScreenFade = sfNone) then
+        SaveThumbnail();
+
+    frame.realTicks:= SDL_GetTicks() - startTime;
+    frame.gameTicks:= GameTicks;
+    frame.CamX:= WorldDx;
+    frame.CamY:= WorldDy - cScreenHeight div 2;
+    frame.zoom:= zoom/cScreenWidth;
+    BlockWrite(cameraFile, frame, 1);
+end;
+
+procedure freeModule;
+begin
+    if flagPrerecording then
+        StopPreRecording();
+end;
+
+end.
+
+{$ENDIF} // USE_VIDEO_RECORDING
--- a/hedgewars/uVisualGears.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uVisualGears.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -135,7 +135,7 @@
     sp: real;
 begin
 AddVisualGear:= nil;
-if ((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet))) and // we are scrolling now
+if ((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) or fastScrolling) and // we are scrolling now
    ((Kind <> vgtCloud) and (not Critical)) then
        exit;
 
@@ -284,8 +284,9 @@
                 begin
                 dx:= 0.005 * (random(15) + 10);
                 dy:= 0.001 * (random(40) + 20);
-                if random(2) = 0 then
-                    dx := -dx;
+                if random(2) = 0 then dx := -dx;
+                if random(2) = 0 then Tag:= 1
+                else Tag:= -1;
                 Frame:= 7 - random(2);
                 FrameTicks:= random(20) + 15;
                 end;
@@ -295,6 +296,8 @@
                 dy:= 0;
                 FrameTicks:= 740;
                 Frame:= 19;
+                Scale:= 0.75;
+                Timer:= 1;
                 end;
     vgtDroplet:
                 begin
@@ -640,9 +643,9 @@
                   vgtSmoke: DrawTextureF(SpritesData[sprSmoke].Texture, Gear^.scale, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, 7 - Gear^.Frame, 1, SpritesData[sprSmoke].Width, SpritesData[sprSmoke].Height);
                   vgtSmokeWhite: DrawSprite(sprSmokeWhite, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame);
                   vgtDust: if Gear^.State = 1 then
-                               DrawSpriteRotatedF(sprSnowDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame, 1, Gear^.Angle)
+                               DrawSpriteRotatedF(sprSnowDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame, Gear^.Tag, Gear^.Angle)
                            else
-                               DrawSprite(sprDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame);
+                               DrawSpriteRotatedF(sprDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame, Gear^.Tag, Gear^.Angle);
                   vgtFire: if (Gear^.State and gstTmpFlag) = 0 then
                                DrawSprite(sprFlame, round(Gear^.X) + WorldDx - 8, round(Gear^.Y) + WorldDy, (RealTicks shr 6 + Gear^.Frame) mod 8)
                            else
@@ -956,10 +959,10 @@
     exit;
 
 if hasBorder or ((Theme <> 'Snow') and (Theme <> 'Christmas')) then
-    for i:= 0 to Pred(vobCount * cScreenSpace div LAND_WIDTH) do
+    for i:= 0 to Pred(vobCount * cScreenSpace div 4096) do
         AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake)
 else
-    for i:= 0 to Pred((vobCount * cScreenSpace div LAND_WIDTH) div 3) do
+    for i:= 0 to Pred((vobCount * cScreenSpace div 4096) div 3) do
         AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake);
 end;
 
--- a/hedgewars/uWorld.pas	Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uWorld.pas	Thu Aug 30 13:02:19 2012 -0400
@@ -60,7 +60,8 @@
     uCaptions,
     uCursor,
     uCommands,
-    uMobile
+    uMobile,
+    uVideoRec
     ;
 
 var cWaveWidth, cWaveHeight: LongInt;
@@ -80,6 +81,7 @@
     stereoDepth: GLfloat;
     isFirstFrame: boolean;
     AMAnimType: LongInt;
+    recTexture: PTexture;
 
 const cStereo_Sky           = 0.0500;
       cStereo_Horizon       = 0.0250;
@@ -377,6 +379,8 @@
     timeTexture:= nil;
     FreeTexture(missionTex);
     missionTex:= nil;
+    FreeTexture(recTexture);
+    recTexture:= nil;
 end;
 
 function GetAmmoMenuTexture(Ammo: PHHAmmo): PTexture;
@@ -954,7 +958,7 @@
     //glPushMatrix;
     //glScalef(1.0, 1.0, 1.0);
 
-    if not isPaused then
+    if (not isPaused) and (GameType <> gmtRecord) then
         MoveCamera;
 
     if cStereoMode = smNone then
@@ -985,7 +989,7 @@
         DrawWorldStereo(0, rmRightEye);
 
         // detatch drawing from fbs
-        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame);
         glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
         SetScale(cDefaultZoomLevel);
 
@@ -1329,7 +1333,7 @@
         r.w:= 3;
         DrawTextureFromRect(TeamHealthBarWidth + 16, cScreenHeight + DrawHealthY + smallScreenOffset, @r, HealthTex);
 
-        if not highlight and not hasGone and (TeamHealth > 1) then
+        if not highlight and (not hasGone) and (TeamHealth > 1) then
             for i:= 0 to cMaxHHIndex do
                 if Hedgehogs[i].Gear <> nil then
                     begin
@@ -1581,6 +1585,33 @@
         end
     end;
 
+{$IFDEF USE_VIDEO_RECORDING}
+// during video prerecording draw red blinking circle and text 'rec'
+if flagPrerecording then
+    begin
+    if recTexture = nil then
+        begin
+        s:= 'rec';
+        tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fntBig].Handle, Str2PChar(s), cWhiteColorChannels);
+        tmpSurface:= doSurfaceConversion(tmpSurface);
+        FreeTexture(recTexture);
+        recTexture:= Surface2Tex(tmpSurface, false);
+        SDL_FreeSurface(tmpSurface)
+        end;
+    DrawTexture( -(cScreenWidth shr 1) + 50, 20, recTexture);
+
+    // draw red circle
+    glDisable(GL_TEXTURE_2D); 
+    Tint($FF, $00, $00, Byte(Round(127*(1 + sin(SDL_GetTicks()*0.007)))));
+    glBegin(GL_POLYGON);
+    for i:= 0 to 20 do
+        glVertex2f(-(cScreenWidth shr 1) + 30 + sin(i*2*Pi/20)*10, 35 + cos(i*2*Pi/20)*10);
+    glEnd();
+    Tint($FF, $FF, $FF, $FF);
+    glEnable(GL_TEXTURE_2D);
+    end;
+{$ENDIF}
+
 SetScale(zoom);
 
 // Cursor
@@ -1614,10 +1645,7 @@
     uCursor.updatePosition();
 {$ENDIF}
 z:= round(200/zoom);
-if not PlacingHogs and (FollowGear <> nil) and (not isCursorVisible) and (not bShowAmmoMenu) and (not fastUntilLag) then
-    if (not autoCameraOn) then
-        FollowGear:= nil
-    else
+if not PlacingHogs and (FollowGear <> nil) and (not isCursorVisible) and (not bShowAmmoMenu) and (not fastUntilLag) and autoCameraOn then
     if ((abs(CursorPoint.X - prevPoint.X) + abs(CursorPoint.Y - prevpoint.Y)) > 4) then
         begin
         FollowGear:= nil;
@@ -1764,8 +1792,14 @@
 if (not cHasFocus) and (GameState <> gsConfirm) then
     ParseCommand('quit', true);
 
-if not cHasFocus then DampenAudio()
-else UndampenAudio();
+{$IFDEF USE_VIDEO_RECORDING}
+// do not change volume during prerecording as it will affect sound in video file
+if not flagPrerecording then
+{$ENDIF}
+    begin
+    if not cHasFocus then DampenAudio()
+    else UndampenAudio();
+    end;
 end;
 
 procedure SetUtilityWidgetState(ammoType: TAmmoType);
@@ -1822,6 +1856,7 @@
 procedure initModule;
 begin
     fpsTexture:= nil;
+    recTexture:= nil;
     FollowGear:= nil;
     WindBarWidth:= 0;
     bShowAmmoMenu:= false;
@@ -1853,7 +1888,9 @@
     FreeTexture(timeTexture);
     timeTexture:= nil;
     FreeTexture(missionTex);
-    missionTex:= nil
+    missionTex:= nil;
+    FreeTexture(recTexture);
+    recTexture:= nil;
 end;
 
 end.
--- a/project_files/hedgewars.pro	Thu Aug 30 12:47:41 2012 -0400
+++ b/project_files/hedgewars.pro	Thu Aug 30 13:02:19 2012 -0400
@@ -104,7 +104,12 @@
     ../QTfrontend/ui/dialog/input_password.h \
     ../QTfrontend/ui/widget/colorwidget.h \
     ../QTfrontend/model/HatModel.h \
-    ../QTfrontend/model/GameStyleModel.h
+    ../QTfrontend/model/GameStyleModel.h \
+    ../QTfrontend/util/libav_iteraction.h \
+    ../QTfrontend/ui/page/pagevideos.h \
+    ../QTfrontend/net/recorder.h \
+    ../QTfrontend/ui/dialog/ask_quit.h \
+    ../QTfrontend/ui/dialog/upload_video.h
 
 SOURCES += ../QTfrontend/model/ammoSchemeModel.cpp \
     ../QTfrontend/model/MapModel.cpp \
@@ -186,7 +191,12 @@
     ../QTfrontend/ui/dialog/input_password.cpp \
     ../QTfrontend/ui/widget/colorwidget.cpp \
     ../QTfrontend/model/HatModel.cpp \
-    ../QTfrontend/model/GameStyleModel.cpp
+    ../QTfrontend/model/GameStyleModel.cpp \
+    ../QTfrontend/util/libav_iteraction.cpp \
+    ../QTfrontend/ui/page/pagevideos.cpp \
+    ../QTfrontend/net/recorder.cpp \
+    ../QTfrontend/ui/dialog/ask_quit.cpp \
+    ../QTfrontend/ui/dialog/upload_video.cpp
 
 win32 {
     SOURCES += ../QTfrontend/xfire.cpp
Binary file share/hedgewars/Data/Graphics/Flags/cpu_plain.png has changed
Binary file share/hedgewars/Data/Graphics/Graves/dragonball.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Graphics/Graves/dragonball.svg	Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<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:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   width="32"
+   height="256"
+   sodipodi:docname="Dragonball.svg"
+   inkscape:export-filename="/home/alumno/Dragonball.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata8">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs6">
+    <linearGradient
+       id="linearGradient4055-9"
+       inkscape:collect="always">
+      <stop
+         id="stop4057-4"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4059-43"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       y2="44.056526"
+       x2="130.48477"
+       y1="44.056526"
+       x1="-9.8052101"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3900-8"
+       xlink:href="#linearGradient4055-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4055-3-0-4"
+       inkscape:collect="always">
+      <stop
+         id="stop4057-6-7-5"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4059-2-5-2"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       y2="44.056526"
+       x2="130.48477"
+       y1="44.056526"
+       x1="-9.8052101"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3488-6"
+       xlink:href="#linearGradient4055-3-0-4"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4055-80"
+       inkscape:collect="always">
+      <stop
+         id="stop4057-7"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4059-1"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       y2="44.056526"
+       x2="130.48477"
+       y1="44.056526"
+       x1="-9.8052101"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4091-2"
+       xlink:href="#linearGradient4055-80"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4055-8-2-8"
+       inkscape:collect="always">
+      <stop
+         id="stop4057-8-7-0"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4059-4-7-8"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4055-8-2-8"
+       id="linearGradient4113-9-1"
+       gradientUnits="userSpaceOnUse"
+       x1="-9.8052101"
+       y1="44.056526"
+       x2="130.48477"
+       y2="44.056526" />
+    <linearGradient
+       id="linearGradient4055-3"
+       inkscape:collect="always">
+      <stop
+         id="stop4057-6"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4059-2"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       y2="44.056526"
+       x2="130.48477"
+       y1="44.056526"
+       x1="-9.8052101"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4091-0"
+       xlink:href="#linearGradient4055-3"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4055-8"
+       inkscape:collect="always">
+      <stop
+         id="stop4057-8"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1;" />
+      <stop
+         id="stop4059-4"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       y2="44.056526"
+       x2="130.48477"
+       y1="44.056526"
+       x1="-9.8052101"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4091-1"
+       xlink:href="#linearGradient4055-8"
+       inkscape:collect="always" />
+  </defs>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1024"
+     inkscape:window-height="576"
+     id="namedview4"
+     showgrid="false"
+     inkscape:zoom="0.92187498"
+     inkscape:cx="9.5337118"
+     inkscape:cy="163.22073"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <g
+     id="g3100">
+    <path
+       transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+       d="m 130.48477,44.056526 a 70.144989,72.407738 0 1 1 -140.2899801,0 70.144989,72.407738 0 1 1 140.2899801,0 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060"
+       style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+       d="m 130.48477,44.056526 a 70.144989,72.407738 0 1 1 -140.2899801,0 70.144989,72.407738 0 1 1 140.2899801,0 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-7"
+       style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:nodetypes="cssscsscc"
+       inkscape:connector-curvature="0"
+       id="path4077"
+       d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+       style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    <path
+       transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+       inkscape:transform-center-y="-0.35951858"
+       inkscape:transform-center-x="0.015096207"
+       d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="1.5590267"
+       sodipodi:arg1="0.93070817"
+       sodipodi:r2="1.9533451"
+       sodipodi:r1="3.9066894"
+       sodipodi:cy="12.666666"
+       sodipodi:cx="13.466667"
+       sodipodi:sides="5"
+       id="path4071"
+       style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="star" />
+  </g>
+  <g
+     transform="translate(-1.5272255e-8,-32.508047)"
+     id="g3100-0">
+    <path
+       transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-1"
+       style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-7-52"
+       style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:nodetypes="cssscsscc"
+       inkscape:connector-curvature="0"
+       id="path4077-67"
+       d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+       style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    <path
+       transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+       inkscape:transform-center-y="-0.35951858"
+       inkscape:transform-center-x="0.015096207"
+       d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="1.5590267"
+       sodipodi:arg1="0.93070817"
+       sodipodi:r2="1.9533451"
+       sodipodi:r1="3.9066894"
+       sodipodi:cy="12.666666"
+       sodipodi:cx="13.466667"
+       sodipodi:sides="5"
+       id="path4071-7"
+       style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="star" />
+  </g>
+  <g
+     transform="translate(-1.5272256e-8,31.848953)"
+     id="g3100-1">
+    <path
+       transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-11"
+       style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-7-9"
+       style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:nodetypes="cssscsscc"
+       inkscape:connector-curvature="0"
+       id="path4077-9"
+       d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+       style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    <path
+       transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+       inkscape:transform-center-y="-0.35951858"
+       inkscape:transform-center-x="0.015096207"
+       d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="1.5590267"
+       sodipodi:arg1="0.93070817"
+       sodipodi:r2="1.9533451"
+       sodipodi:r1="3.9066894"
+       sodipodi:cy="12.666666"
+       sodipodi:cx="13.466667"
+       sodipodi:sides="5"
+       id="path4071-51"
+       style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="star" />
+  </g>
+  <g
+     id="g3100-5"
+     transform="translate(0,96.26138)">
+    <path
+       transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-8"
+       style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-7-86"
+       style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:nodetypes="cssscsscc"
+       inkscape:connector-curvature="0"
+       id="path4077-4"
+       d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+       style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    <path
+       transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+       inkscape:transform-center-y="-0.35951858"
+       inkscape:transform-center-x="0.015096207"
+       d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="1.5590267"
+       sodipodi:arg1="0.93070817"
+       sodipodi:r2="1.9533451"
+       sodipodi:r1="3.9066894"
+       sodipodi:cy="12.666666"
+       sodipodi:cx="13.466667"
+       sodipodi:sides="5"
+       id="path4071-5"
+       style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="star" />
+  </g>
+  <g
+     transform="translate(-1.5272255e-8,63.753333)"
+     id="g3100-0-0">
+    <path
+       transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-1-1"
+       style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-7-52-0"
+       style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:nodetypes="cssscsscc"
+       inkscape:connector-curvature="0"
+       id="path4077-67-5"
+       d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+       style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    <path
+       transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+       inkscape:transform-center-y="-0.35951858"
+       inkscape:transform-center-x="0.015096207"
+       d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="1.5590267"
+       sodipodi:arg1="0.93070817"
+       sodipodi:r2="1.9533451"
+       sodipodi:r1="3.9066894"
+       sodipodi:cy="12.666666"
+       sodipodi:cx="13.466667"
+       sodipodi:sides="5"
+       id="path4071-7-0"
+       style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="star" />
+  </g>
+  <g
+     transform="translate(-1.5272256e-8,128.11033)"
+     id="g3100-1-8">
+    <path
+       transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-11-9"
+       style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-7-9-4"
+       style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:nodetypes="cssscsscc"
+       inkscape:connector-curvature="0"
+       id="path4077-9-1"
+       d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+       style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    <path
+       transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+       inkscape:transform-center-y="-0.35951858"
+       inkscape:transform-center-x="0.015096207"
+       d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="1.5590267"
+       sodipodi:arg1="0.93070817"
+       sodipodi:r2="1.9533451"
+       sodipodi:r1="3.9066894"
+       sodipodi:cy="12.666666"
+       sodipodi:cx="13.466667"
+       sodipodi:sides="5"
+       id="path4071-51-2"
+       style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="star" />
+  </g>
+  <g
+     id="g3100-5-3"
+     transform="translate(-1.5272253e-8,160.30523)">
+    <path
+       transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-8-8"
+       style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+       d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+       sodipodi:ry="72.407738"
+       sodipodi:rx="70.144989"
+       sodipodi:cy="44.056526"
+       sodipodi:cx="60.339779"
+       id="path3060-7-86-6"
+       style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="arc" />
+    <path
+       sodipodi:nodetypes="cssscsscc"
+       inkscape:connector-curvature="0"
+       id="path4077-4-0"
+       d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+       style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+    <path
+       transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+       inkscape:transform-center-y="-0.35951858"
+       inkscape:transform-center-x="0.015096207"
+       d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+       inkscape:randomized="0"
+       inkscape:rounded="0"
+       inkscape:flatsided="false"
+       sodipodi:arg2="1.5590267"
+       sodipodi:arg1="0.93070817"
+       sodipodi:r2="1.9533451"
+       sodipodi:r1="3.9066894"
+       sodipodi:cy="12.666666"
+       sodipodi:cx="13.466667"
+       sodipodi:sides="5"
+       id="path4071-5-7"
+       style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       sodipodi:type="star" />
+  </g>
+  <g
+     transform="translate(8.8208575,190.52706)"
+     id="g3100-1-8-8">
+    <g
+       id="g3339"
+       transform="translate(-8.8208575,1.5340622)">
+      <path
+         sodipodi:type="arc"
+         style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path3060-11-9-8"
+         sodipodi:cx="60.339779"
+         sodipodi:cy="44.056526"
+         sodipodi:rx="70.144989"
+         sodipodi:ry="72.407738"
+         d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+         transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)" />
+      <path
+         sodipodi:type="arc"
+         style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path3060-7-9-4-8"
+         sodipodi:cx="60.339779"
+         sodipodi:cy="44.056526"
+         sodipodi:rx="70.144989"
+         sodipodi:ry="72.407738"
+         d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+         transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)" />
+      <path
+         style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+         id="path4077-9-1-3"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cssscsscc" />
+      <path
+         sodipodi:type="star"
+         style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path4071-51-2-5"
+         sodipodi:sides="5"
+         sodipodi:cx="13.466667"
+         sodipodi:cy="12.666666"
+         sodipodi:r1="3.9066894"
+         sodipodi:r2="1.9533451"
+         sodipodi:arg1="0.93070817"
+         sodipodi:arg2="1.5590267"
+         inkscape:flatsided="false"
+         inkscape:rounded="0"
+         inkscape:randomized="0"
+         d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+         inkscape:transform-center-x="0.015096207"
+         inkscape:transform-center-y="-0.35951858"
+         transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)" />
+    </g>
+  </g>
+  <path
+     style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+     d="m 5.6818853,52.944892 c -1.4249208,-4.670814 -0.1790584,-8.097449 1.725719,-10.495306 2.0559768,-2.588197 5.0634737,-4.368771 8.4063917,-4.281193 2.450497,-0.111055 5.480318,1.374625 5.480318,1.374625 -0.07868,0.78593 -6.13236,2.602482 -8.919506,5.655618 -2.283219,2.501117 -5.0641461,7.081358 -4.7383419,11.096412 -0.6809241,-0.87477 -1.4647048,-2.11081 -1.9545808,-3.350156 z"
+     id="path4121-4"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="cscsscc" />
+  <path
+     style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+     d="M 5.9583102,85.256922 C 5.4238859,83.650628 4.4751276,79.169801 5.5109031,77.40561 6.5395157,75.653619 7.397708,73.991223 8.317305,73.071625 c 1.079063,-1.079062 3.838619,-3.17952 9.462215,-2.843943 1.806813,0.204535 4.457553,1.7441 4.457553,1.7441 -0.07868,0.78593 -5.183207,3.531776 -7.970353,6.584911 -2.14499,2.349697 -4.7242734,7.983314 -6.5030137,9.559525 -0.3693871,-0.482911 -1.133301,-1.17148 -1.8053961,-2.859296 z"
+     id="path4121-4-6"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="csscsscc" />
+  <path
+     style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+     d="m 6.0993866,117.32562 c 3.5759918,-9.01347 6.7157204,-11.897 11.9170884,-14.69367 1.806813,-0.27485 4.361674,1.40853 4.361674,1.40853 -0.07868,0.78593 -4.173901,3.91587 -6.961045,6.96901 -2.708171,2.96662 -6.4118227,14.10633 -9.3177174,6.31613 z"
+     id="path4121-4-6-5"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccssc" />
+  <path
+     style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+     d="m 8.0353418,152.69557 c 8.9452112,-4.69892 12.8040302,-14.72543 12.7320592,-16.85095 1.758874,0.25249 4.335587,3.83076 4.335587,3.83076 -0.07868,0.78593 -1.835084,6.44844 -4.622233,9.50157 -1.983167,2.17243 -5.268932,6.41929 -8.885039,5.90193 -1.32197,-0.18914 -2.1543301,-0.80776 -3.5603742,-2.38331 z"
+     id="path4121-4-6-5-9"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccsscc" />
+  <path
+     style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+     d="m 10.441003,186.42942 c 3.222707,-1.69289 5.890978,-4.0906 7.895906,-6.56495 3.560116,-4.39366 5.17465,-9.02893 5.128608,-10.38869 1.423297,1.21127 2.90704,4.80317 2.90704,4.80317 0.395896,2.95542 0.537798,6.99081 -2.249351,10.04395 -2.060273,2.25689 -5.592916,3.90658 -9.359327,3.58681 -1.248968,-0.10604 -4.171671,-1.08589 -4.322876,-1.48029 z"
+     id="path4121-4-6-5-9-2"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="cscsssc" />
+  <path
+     style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+     d="m 13.077671,219.87312 c 8.945211,-4.69892 10.987149,-15.6465 10.915178,-17.77202 1.423298,1.01952 2.379707,4.51894 2.379707,4.51894 -0.07868,0.78593 0.864467,4.39019 -1.055682,8.05118 -0.783125,1.49312 -3.834308,4.50955 -7.291243,5.31355 -1.302673,0.30297 -3.01998,0.17502 -4.94796,-0.11165 z"
+     id="path4121-4-6-5-9-2-5"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccsssc" />
+</svg>
Binary file share/hedgewars/Data/Graphics/botlevels.png has changed
--- a/share/hedgewars/Data/Locale/en.txt	Thu Aug 30 12:47:41 2012 -0400
+++ b/share/hedgewars/Data/Locale/en.txt	Thu Aug 30 13:02:19 2012 -0400
@@ -492,7 +492,7 @@
 04:36=Well, sometimes you're just too bad in aiming. Get|some assistance using modern day technology.|Attack: Activate
 04:37=Don't fear the daylight. It will just last one turn|but will enable you to absorb the damage you do to|other hogs.|Attack: Activate
 04:38=The sniper rifle can be the most devastating weapon|in your whole arsenal, however it's very ineffective|at close quarters. The damage dealt increases with|the distance to its target.|Attack: Shoot (twice)
-04:39=Fly to other parts of the map using the flying|saucer. This hard to master utility is able to|take you to almost any position on the battlefield.|Attack: Activate|Up/Left/Right: Apply force in one direction|Long Jump: Drop grenades or similar weapons
+04:39=Fly to other parts of the map using the flying|saucer. This hard to master utility can|take you to almost any position on the battlefield.|Attack: Activate|Up/Left/Right: Apply force in one direction|Long Jump: Drop grenades or similar weapons
 04:40=Set some ground on fire using this bottle filled|with (soon to be) burning liquid.|Attack: Hold to shoot with more power
 04:41=The evidence nature might even top the flying|saucer. Birdy can carry your hog around and|drop eggs on your enemies!|Be quick, as using Birdy eats into your turn|time!|Attack: Activate and drop eggs|Up/Left/Right: Flap in one direction
 04:42=This portable portal device is capable|of instantly transporting you, your enemies,|or your weaponry between two points on the|terrain.|Use it wisely and your campaign will be a...|HUGE SUCCESS!|Attack: Shoot a portal|Switch: Cycle portal colours
@@ -506,7 +506,7 @@
 04:50=Is someone hiding underground?|Dig them out with a drill strike!|Timer controls how far it will dig.
 04:51=Get in a free shot by hurling a ball of mud.|Stings a bit, and knocks hogs back.
 04:52=UNUSED
-04:53=Go on an adventure through time and space,|while leaving your comrades to fight on alone.|Be prepared to return at any time,|or for Sudden Death or if they are all defeated.|Disclaimer. Does not function in Sudden Death,|if you are alone, or if you are a King.
+04:53=Take a trip through time and space,|while leaving your comrades to fight on alone.|Be prepared to return at any time,|or for Sudden Death or if they are all defeated.|Disclaimer. Does not function in Sudden Death,|if you are alone, or if you are a King.
 04:54=INCOMPLETE                                                                                                                                     
 04:55=Spray a stream of sticky flakes.|Build bridges, bury enemies, seal off tunnels.|Be careful you don't get any on you!
 
--- a/share/hedgewars/Data/Locale/hedgewars_zh_CN.ts	Thu Aug 30 12:47:41 2012 -0400
+++ b/share/hedgewars/Data/Locale/hedgewars_zh_CN.ts	Thu Aug 30 13:02:19 2012 -0400
@@ -2,325 +2,240 @@
 <!DOCTYPE TS>
 <TS version="2.0" language="zh_CN">
 <context>
-    <name>AmmoSchemeModel</name>
-    <message>
-        <source>new</source>
-        <translation>新建</translation>
-    </message>
-    <message>
-        <source>copy of</source>
-        <translation>副本</translation>
-    </message>
-</context>
-<context>
-    <name>DrawMapWidget</name>
-    <message>
-        <source>File error</source>
-        <translation>文件错误</translation>
-    </message>
-    <message>
-        <source>Cannot open file &apos;%1&apos; for writing</source>
-        <translatorcomment>无法打开文件 &apos;%1&apos; 写入</translatorcomment>
-        <translation>无法打开要写入的文件 &apos;%1&apos;</translation>
-    </message>
-    <message>
-        <source>Cannot read file &apos;%1&apos;</source>
-        <translation>无法读取文件 &apos;%1&apos;</translation>
-    </message>
-</context>
-<context>
     <name>FreqSpinBox</name>
     <message>
+        <location filename="../../../../QTfrontend/misc.h" line="38"/>
         <source>Never</source>
         <translation>从不</translation>
     </message>
     <message numerus="yes">
+        <location filename="../../../../QTfrontend/misc.h" line="40"/>
         <source>Every %1 turn</source>
         <translation>
-            <numerusform>每 %1 个回合</numerusform>
+            <numerusform>每隔 %1 回合</numerusform>
         </translation>
     </message>
 </context>
 <context>
     <name>GameCFGWidget</name>
     <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="129"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="129"/>
         <source>Illegal ammo scheme</source>
         <translation>无法使用此弹药设置</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="57"/>
         <source>Edit schemes</source>
         <translation>修改游戏设置</translation>
     </message>
-    <message>
-        <source>Edit weapons</source>
-        <translation>修改武器</translation>
-    </message>
-    <message>
-        <source>When this option is enabled selecting a game scheme will auto-select a weapon</source>
-        <translation>使用此项则游戏框架自动选择武器配备</translation>
-    </message>
-    <message>
-        <source>Game Options</source>
-        <translation>游戏选项</translation>
-    </message>
 </context>
 <context>
-    <name>HWChatWidget</name>
-    <message>
-        <source>%1 *** %2 has been removed from your ignore list</source>
-        <translation type="obsolete">%1 *** %2 已经从您的忽略列表中移除</translation>
-    </message>
-    <message>
-        <source>%1 *** %2 has been added to your ignore list</source>
-        <translation type="obsolete">%1 *** %2 已经添加到您的忽略列表中</translation>
-    </message>
-    <message>
-        <source>%1 *** %2 has been removed from your friends list</source>
-        <translation type="obsolete">%1 *** %2 已经从您的朋友列表中移除</translation>
-    </message>
+    <name>GameUIConfig</name>
     <message>
-        <source>%1 *** %2 has been added to your friends list</source>
-        <translation type="obsolete">%1 *** %2 已经添加到您的朋友列表中</translation>
-    </message>
-    <message>
-        <source>%1 has been removed from your ignore list</source>
-        <translation>%1 已从您的忽略列表中移除</translation>
-    </message>
-    <message>
-        <source>%1 has been added to your ignore list</source>
-        <translation>%1 已被添加到您的忽略列表中</translation>
-    </message>
-    <message>
-        <source>%1 has been removed from your friends list</source>
-        <translation>%1 已从您的好友列表中移除</translation>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+        <source>Error</source>
+        <translation type="obsolete">错误</translation>
     </message>
     <message>
-        <source>%1 has been added to your friends list</source>
-        <translation>%1 已加入您的好友列表</translation>
-    </message>
-    <message>
-        <source>Stylesheet imported from %1</source>
-        <translation>已从 %1 导入样式表</translation>
-    </message>
-    <message>
-        <source>Enter %1 if you want to use the current StyleSheet in future, enter %2 to reset!</source>
-        <translation>键入 %1 以使用当前的样式表,键入 %2 以重置!</translation>
-    </message>
-    <message>
-        <source>Couldn&apos;t read %1</source>
-        <translation>无法读取 %1</translation>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+        <source>Cannot create directory %1</source>
+        <translation type="obsolete">不能创建路径</translation>
     </message>
     <message>
-        <source>StyleSheet discarded</source>
-        <translation>已丢弃样式表</translation>
-    </message>
-    <message>
-        <source>StyleSheet saved to %1</source>
-        <translation>样式表已保存到 %1</translation>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+        <source>Quit</source>
+        <translation type="obsolete">退出</translation>
     </message>
     <message>
-        <source>Failed to save StyleSheet to %1</source>
-        <translation>保存样式表到 %1 失败</translation>
-    </message>
-    <message>
-        <source>%1 is not a valid command!</source>
-        <translation>%1 不是一个有效的命令!</translation>
-    </message>
-    <message>
-        <source>Kicking %1 ...</source>
-        <translation>正在踢出 %1 ...</translation>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+        <source>Cannot save options to file %1</source>
+        <translation type="obsolete">不能把选项保存到 %1</translation>
     </message>
 </context>
 <context>
     <name>HWForm</name>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="639"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+        <source>Please, select demo from the list above</source>
+        <translation type="obsolete">请选择一个DEMO</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="484"/>
         <source>OK</source>
         <translation>确认</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+        <source>Please, select server from the list above</source>
+        <translation type="obsolete">请选择一个服务器</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="483"/>
+        <source>Please, select record from the list above</source>
+        <translation>请选择一个记录</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="794"/>
         <source>Cannot save record to file %1</source>
         <translation>无法录入文件 %1</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="640"/>
         <source>Unable to start the server</source>
         <translation>开启服务端出现错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="256"/>
         <source>new</source>
-        <translation type="obsolete">新</translation>
-    </message>
-    <message>
-        <source>Please select record from the list above</source>
-        <translation>请选择一个记录</translation>
-    </message>
-    <message>
-        <source>DefaultTeam</source>
-        <translation>默认队伍</translation>
-    </message>
-    <message>
-        <source>Hedgewars Demo File</source>
-        <comment>File Types</comment>
-        <translation>刺猬大作战回放文件</translation>
-    </message>
-    <message>
-        <source>Hedgewars Save File</source>
-        <comment>File Types</comment>
-        <translation>刺猬大作战存档文件</translation>
-    </message>
-    <message>
-        <source>Demo name</source>
-        <translation>回放录像名称</translation>
-    </message>
-    <message>
-        <source>Demo name:</source>
-        <translation>回放录像名称:</translation>
-    </message>
-    <message>
-        <source>Game aborted</source>
-        <translation>游戏被中断</translation>
-    </message>
-    <message>
-        <source>Password</source>
-        <translation>密码</translation>
-    </message>
-    <message>
-        <source>Your nickname %1 is
-registered on Hedgewars.org
-Please provide your password below
-or pick another nickname in game config:</source>
-        <translation>您的昵称%1
-在Hedgewars.org已注册
-请输入您的密码
-或从游戏配置中选择另一个昵称:</translation>
-    </message>
-    <message>
-        <source>No password supplied.</source>
-        <translation>没有填写密码.</translation>
-    </message>
-    <message>
-        <source>Nickname</source>
-        <translation>昵称</translation>
-    </message>
-    <message>
-        <source>Some one already uses
- your nickname %1
-on the server.
-Please pick another nickname:</source>
-        <translation>您的昵称 %1
-以被其他人
-在服务器上使用
-请选择另一个昵称:</translation>
-    </message>
-    <message>
-        <source>No nickname supplied.</source>
-        <translation>没有填写昵称。</translation>
+        <translation>新</translation>
     </message>
 </context>
 <context>
     <name>HWGame</name>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+        <source>Error</source>
+        <translation type="obsolete">错误</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+        <source>Unable to start the server: %1.</source>
+        <translation type="obsolete">开启服务端出现错误: %1.</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/game.cpp" line="280"/>
         <source>en.txt</source>
         <translation>zh_CN.txt</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/game.cpp" line="0"/>
+        <source>Cannot save demo to file %1</source>
+        <translation type="obsolete">不能把demo保存为 %1</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/game.cpp" line="0"/>
+        <source>Quit</source>
+        <translation type="obsolete">退出</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/game.cpp" line="305"/>
         <source>Cannot open demofile %1</source>
         <translation>DEMO %1 打不开</translation>
     </message>
+    <message>
+        <location filename="../../../../QTfrontend/game.cpp" line="0"/>
+        <source>Unable to run engine: %1 (</source>
+        <translation type="obsolete">引擎无法启动: %1 (</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/game.cpp" line="147"/>
+        <source>Error reading training config file</source>
+        <translation>训练设置文件无法读取</translation>
+    </message>
 </context>
 <context>
     <name>HWMapContainer</name>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="90"/>
         <source>Map</source>
         <translation>地图</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="110"/>
         <source>Themes</source>
         <translation>主题</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="93"/>
         <source>Filter</source>
         <translation>过滤</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="97"/>
         <source>All</source>
         <translation>全部</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="98"/>
         <source>Small</source>
         <translation>小型</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="99"/>
         <source>Medium</source>
         <translation>中型</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="100"/>
         <source>Large</source>
         <translation>大型</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="101"/>
         <source>Cavern</source>
         <translation>洞穴</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="102"/>
         <source>Wacky</source>
-        <translation>险峻</translation>
-    </message>
-    <message>
-        <source>Type</source>
-        <translation>类型</translation>
+        <translation>曲折</translation>
     </message>
+</context>
+<context>
+    <name>HWNet</name>
     <message>
-        <source>Small tunnels</source>
-        <translation>小型隧道</translation>
-    </message>
-    <message>
-        <source>Medium tunnels</source>
-        <translation>中型隧道</translation>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+        <source>Error</source>
+        <translation type="obsolete">错误</translation>
     </message>
     <message>
-        <source>Large tunnels</source>
-        <translation>大型隧道</translation>
-    </message>
-    <message>
-        <source>Small floating islands</source>
-        <translation>小型漂浮岛屿</translation>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+        <source>The host was not found. Please check the host name and port settings.</source>
+        <translation type="obsolete">未发现主机。请检查主机名和端口设置。</translation>
     </message>
     <message>
-        <source>Medium floating islands</source>
-        <translation>中型漂浮岛屿</translation>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+        <source>Connection refused</source>
+        <translation type="obsolete">连接被拒绝</translation>
+    </message>
+</context>
+<context>
+    <name>HWNetServer</name>
+    <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+        <source>Error</source>
+        <translation type="obsolete">错误</translation>
     </message>
     <message>
-        <source>Large floating islands</source>
-        <translation>大型漂浮岛屿</translation>
-    </message>
-    <message>
-        <source>Seed</source>
-        <translation>作种</translation>
-    </message>
-    <message>
-        <source>Set</source>
-        <translation>设定</translation>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+        <source>Unable to start the server: %1.</source>
+        <translation type="obsolete">无法启动服务端: %1.</translation>
     </message>
 </context>
 <context>
     <name>HWNetServersModel</name>
     <message>
+        <location filename="../../../../QTfrontend/netserverslist.cpp" line="45"/>
         <source>Title</source>
-        <translation>名称</translation>
+        <translation>标题</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/netserverslist.cpp" line="46"/>
         <source>IP</source>
         <translation>IP</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/netserverslist.cpp" line="47"/>
         <source>Port</source>
         <translation>端口</translation>
     </message>
@@ -328,177 +243,109 @@
 <context>
     <name>HWNewNet</name>
     <message>
-        <source>The host was not found. Please check the host name and port settings.</source>
-        <translation>没找到主机。请检查主机名和端口设置。</translation>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="187"/>
+        <source>Error</source>
+        <translation type="obsolete">错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="192"/>
+        <source>The host was not found. Please check the host name and port settings.</source>
+        <translation>错误没找到这个主机。请检查主机名和端口设置。</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="195"/>
         <source>Connection refused</source>
         <translation>连接被拒绝</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="402"/>
+        <source>*** %1 joined</source>
+        <translation>*** %1 加入</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="439"/>
+        <source>*** %1 left</source>
+        <translation>*** %1 离开</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="441"/>
+        <source>*** %1 left (%2)</source>
+        <translation>*** %1 离开 (%2)</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="531"/>
         <source>Quit reason: </source>
         <translation>退出原因:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="350"/>
         <source>Room destroyed</source>
         <translation>房间损坏</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="357"/>
         <source>You got kicked</source>
         <translation>被踢出</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="452"/>
         <source>Password</source>
-        <translation type="obsolete">密码</translation>
-    </message>
-    <message>
-        <source>Your nickname %1 is
-registered on Hedgewars.org
-Please provide your password
-or pick another nickname:</source>
-        <translation type="obsolete">您的昵称%1
-在Hedgewars.org已注册
-请输入您的密码
-或选择另一个昵称:</translation>
-    </message>
-    <message>
-        <source>%1 *** %2 has joined the room</source>
-        <translation>%1 *** %2 进入这个房间了</translation>
-    </message>
-    <message>
-        <source>%1 *** %2 has joined</source>
-        <translation>%1 *** %2 加入了</translation>
+        <translation>密码</translation>
     </message>
     <message>
-        <source>%1 *** %2 has left (%3)</source>
-        <translation>%1 *** %2 离开了(%3)</translation>
-    </message>
-    <message>
-        <source>%1 *** %2 has left</source>
-        <translation>%1 *** %2 离开了</translation>
-    </message>
-    <message>
-        <source>Your nickname %1 is
-registered on Hedgewars.org
-Please provide your password below
-or pick another nickname in game config:</source>
-        <translation type="obsolete">您的昵称%1
-在Hedgewars.org已注册
-请输入您的密码
-或从游戏配置中选择另一个昵称:</translation>
-    </message>
-    <message>
-        <source>Nickname</source>
-        <translation type="obsolete">昵称</translation>
-    </message>
-    <message>
-        <source>User quit</source>
-        <translation>用户退出</translation>
+        <location filename="../../../../QTfrontend/newnetclient.cpp" line="452"/>
+        <source>Enter your password:</source>
+        <translation>输入你的密码:</translation>
     </message>
 </context>
 <context>
     <name>KB</name>
     <message>
+        <location filename="../../../../QTfrontend/KB.h" line="32"/>
         <source>SDL_ttf returned error while rendering text, most propably it is related to the bug in freetype2. It&apos;s recommended to update your freetype lib.</source>
-        <translation>渲染文字时SDL_ttf 返回错误,可能有关freetype2的bug。建议升级 freetype。</translation>
+        <translation>SDL_ttf 返回错误-渲染文字失败,可能有关freetype2的bug。建议升级 freetype。</translation>
     </message>
 </context>
 <context>
     <name>PageAdmin</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="1063"/>
         <source>Server message:</source>
-        <translation type="obsolete">服务器信息:</translation>
-    </message>
-    <message>
-        <source>Set message</source>
-        <translation type="obsolete">设定信息</translation>
-    </message>
-    <message>
-        <source>Clear Accounts Cache</source>
-        <translation>清空账户缓存</translation>
-    </message>
-    <message>
-        <source>Fetch data</source>
-        <translation>获取数据</translation>
+        <translation>服务器信息:</translation>
     </message>
     <message>
-        <source>Server message for latest version:</source>
-        <translation>最新版本的服务器信息:</translation>
-    </message>
-    <message>
-        <source>Server message for previous versions:</source>
-        <translation>之前版本的服务器信息:</translation>
-    </message>
-    <message>
-        <source>Latest version protocol number:</source>
-        <translation>最新版本的通讯协议号码:</translation>
-    </message>
-    <message>
-        <source>MOTD preview:</source>
-        <translation>MOTD预览:</translation>
-    </message>
-    <message>
-        <source>Set data</source>
-        <translation>设定数据</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="1069"/>
+        <source>Set message</source>
+        <translation>设定信息</translation>
     </message>
 </context>
 <context>
     <name>PageConnecting</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="848"/>
         <source>Connecting...</source>
         <translation>连接中...</translation>
     </message>
-    <message>
-        <source>Cancel</source>
-        <translation type="obsolete">取消</translation>
-    </message>
-</context>
-<context>
-    <name>PageDrawMap</name>
-    <message>
-        <source>Undo</source>
-        <translation>取消</translation>
-    </message>
-    <message>
-        <source>Clear</source>
-        <translation>清除</translation>
-    </message>
-    <message>
-        <source>Load</source>
-        <translation>读取</translation>
-    </message>
-    <message>
-        <source>Save</source>
-        <translation>保存</translation>
-    </message>
-    <message>
-        <source>Load drawn map</source>
-        <translation>读取已经绘制的地图</translation>
-    </message>
-    <message>
-        <source>Drawn Maps (*.hwmap);;All files (*.*)</source>
-        <translation type="obsolete">绘制的地图 (*.hwmap);;全部文件 (*.*)</translation>
-    </message>
-    <message>
-        <source>Save drawn map</source>
-        <translation>保存绘制的地图</translation>
-    </message>
-    <message>
-        <source>Drawn Maps</source>
-        <translation>绘制的地图</translation>
-    </message>
-    <message>
-        <source>All files</source>
-        <translation>全部文件</translation>
-    </message>
 </context>
 <context>
     <name>PageEditTeam</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Discard</source>
+        <translation type="obsolete">中止</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Save</source>
+        <translation type="obsolete">保存</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="106"/>
         <source>General</source>
         <translation>常规</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="107"/>
         <source>Advanced</source>
         <translation>进阶</translation>
     </message>
@@ -506,393 +353,87 @@
 <context>
     <name>PageGameStats</name>
     <message>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="101"/>
         <source>&lt;p&gt;The best shot award was won by &lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; pts.&lt;/p&gt;</source>
-        <translation type="obsolete">&lt;p&gt;最佳射手奖给与 &lt;b&gt;%1&lt;/b&gt;:伤害 &lt;b&gt;%2&lt;/b&gt;点。&lt;/p&gt;</translation>
-    </message>
-    <message numerus="yes">
-        <source>&lt;p&gt;The best killer is &lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; kills in a turn.&lt;/p&gt;</source>
-        <translation type="obsolete">
-            <numerusform>&lt;p&gt;最佳杀手是 &lt;b&gt;%1&lt;/b&gt;单回合击杀刺猬数:&lt;b&gt;%2&lt;/b&gt;&lt;/p&gt;</numerusform>
-        </translation>
+        <translation>&lt;p&gt;最佳射手是&lt;b&gt;%1&lt;/b&gt;。伤害 &lt;b&gt;%2&lt;/b&gt;点。&lt;/p&gt;</translation>
     </message>
     <message numerus="yes">
-        <source>&lt;p&gt;A total of &lt;b&gt;%1&lt;/b&gt; hedgehog(s) were killed during this round.&lt;/p&gt;</source>
-        <translation type="obsolete">
-            <numerusform>&lt;p&gt;本轮总共有&lt;b&gt;%1&lt;/b&gt;只刺猬被击杀&lt;/p&gt;</numerusform>
-        </translation>
-    </message>
-    <message>
-        <source>Details</source>
-        <translation>细节</translation>
-    </message>
-    <message>
-        <source>Health graph</source>
-        <translation>健康值图形</translation>
-    </message>
-    <message>
-        <source>Ranking</source>
-        <translation>排名</translation>
-    </message>
-    <message>
-        <source>The best shot award was won by &lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; pts.</source>
-        <translation>最佳射手&lt;b&gt;%1&lt;/b&gt;制造了&lt;b&gt;%2&lt;/b&gt;点伤害。</translation>
-    </message>
-    <message numerus="yes">
-        <source>The best killer is &lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; kills in a turn.</source>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="108"/>
+        <source>&lt;p&gt;The best killer is &lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; kills in a turn.&lt;/p&gt;</source>
         <translation>
-            <numerusform>最佳杀手 &lt;b&gt;%1&lt;/b&gt; 完成了单回合&lt;b&gt;%2&lt;/b&gt;次击杀。</numerusform>
+            <numerusform>&lt;p&gt;最佳杀手&lt;b&gt;%1&lt;/b&gt;:却敌&lt;b&gt;%2&lt;/b&gt;&lt;/p&gt;</numerusform>
         </translation>
     </message>
     <message numerus="yes">
-        <source>A total of &lt;b&gt;%1&lt;/b&gt; hedgehog(s) were killed during this round.</source>
-        <translation>
-            <numerusform>总共 &lt;b&gt;%1&lt;/b&gt; 只刺猬在本轮失去生命。</numerusform>
-        </translation>
-    </message>
-    <message numerus="yes">
-        <source>(%1 kill)</source>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="115"/>
+        <source>&lt;p&gt;A total of &lt;b&gt;%1&lt;/b&gt; hedgehog(s) were killed during this round.&lt;/p&gt;</source>
         <translation>
-            <numerusform>(%1 击杀)</numerusform>
-        </translation>
-    </message>
-    <message>
-        <source>(%1 kills)</source>
-        <translation type="obsolete">(%1 击杀)</translation>
-    </message>
-    <message numerus="yes">
-        <source>&lt;b&gt;%1&lt;/b&gt; thought it&apos;s good to shoot his own hedgehogs with &lt;b&gt;%2&lt;/b&gt; pts.</source>
-        <translation>
-            <numerusform>&lt;b&gt;%1&lt;/b&gt;以为给自己的刺猬造成 &lt;b&gt;%2&lt;/b&gt; 点创伤是小意思。</numerusform>
+            <numerusform>&lt;p&gt;有&lt;b&gt;%1&lt;/b&gt;个刺猬在此局失去生命。&lt;/p&gt;</numerusform>
         </translation>
     </message>
-    <message numerus="yes">
-        <source>&lt;b&gt;%1&lt;/b&gt; killed &lt;b&gt;%2&lt;/b&gt; of his own hedgehogs.</source>
-        <translation>
-            <numerusform>&lt;b&gt;%1&lt;/b&gt; 整垮了 &lt;b&gt;%2&lt;/b&gt; 只自己的刺猬。</numerusform>
-        </translation>
-    </message>
-    <message numerus="yes">
-        <source>&lt;b&gt;%1&lt;/b&gt; was scared and skipped turn &lt;b&gt;%2&lt;/b&gt; times.</source>
-        <translation>
-            <numerusform>&lt;b&gt;%1&lt;/b&gt; 受惊了,共计 &lt;b&gt;%2&lt;/b&gt; 次放弃。</numerusform>
-        </translation>
-    </message>
-</context>
-<context>
-    <name>PageInGame</name>
-    <message>
-        <source>In game...</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>PageMain</name>
     <message>
-        <source>Local Game (Play a game on a single computer)</source>
-        <translation>单机游戏(在一台电脑上)</translation>
-    </message>
-    <message>
-        <source>Network Game (Play a game across a network)</source>
-        <translation>网络游戏(通过网络)</translation>
-    </message>
-    <message>
-        <source>Simply pick the same color as a friend to play together as a team. Each of you will still control his or her own hedgehogs but they&apos;ll win or lose together.</source>
-        <comment>Tips</comment>
-        <translation>点击同色作为同一组的友军。控制权不分享,但是共同胜利/失败。</translation>
-    </message>
-    <message>
-        <source>Some weapons might do only low damage but they can be a lot more devastating in the right situation. Try to use the Desert Eagle to knock multiple hedgehogs into the water.</source>
-        <comment>Tips</comment>
-        <translation>有些武器可能威力低下但是有毁灭性的效果。比如沙漠之鹰能把多个刺猬打入水中。</translation>
-    </message>
-    <message>
-        <source>If you&apos;re unsure what to do and don&apos;t want to waste ammo, skip one round. But don&apos;t let too much time pass as there will be Sudden Death!</source>
-        <comment>Tips</comment>
-        <translation>加入不确定怎么做,不要浪费弹药,跳过此回合。但是注意突然时间!</translation>
-    </message>
-    <message>
-        <source>Want to save ropse? Release the rope in mid air and then shoot again. As long as you don&apos;t touch the ground you&apos;ll reuse your rope without wasting ammo!</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">保存绳子?在半空释放然后再次射出。只要不接触地面停止就可以继续使用同一根不会浪费!</translation>
-    </message>
-    <message>
-        <source>If you&apos;d like to keep others from using your preferred nickname on the official server, register an account at http://www.hedgewars.org/.</source>
-        <comment>Tips</comment>
-        <translation>如果您确定好了一个昵称不想让别人使用,那么在  http://www.hedgewars.org/. 注册一个帐号吧。</translation>
-    </message>
-    <message>
-        <source>You&apos;re bored of default gameplay? Try one of the missions - they&apos;ll offer different gameplay depending on the one you picked.</source>
-        <comment>Tips</comment>
-        <translation>厌倦了默认的玩法?试试任务吧——每个任务都有不同的玩法。</translation>
-    </message>
-    <message>
-        <source>By default the game will always record the last game played as a demo. Select &apos;Local Game&apos; and pick the &apos;Demos&apos; button on the lower right corner to play or manage them.</source>
-        <comment>Tips</comment>
-        <translation>默认情况下游戏记录最后的游戏作为Demo,选择单机游戏——然后Demo——然后点击右下角开始回放或者整理。</translation>
-    </message>
-    <message>
-        <source>Hedgewars is Open Source and Freeware we create in our spare time. If you&apos;ve got problems, ask on our forums but please don&apos;t expect 24/7 support!</source>
-        <comment>Tips</comment>
-        <translation>刺猬大作战是一个开放源代码的免费软件,它充分利用了我们的业余时间。如果您有问题,到论坛来吧,不过7×24小时支持不可能!</translation>
-    </message>
-    <message>
-        <source>Hedgewars is Open Source and Freeware we create in our spare time. If you like it, help us with a small donation or contribute your own work!</source>
-        <comment>Tips</comment>
-        <translation>刺猬大作战是一个开放源代码的免费软件,它充分利用了我们的业余时间。如果您喜欢它,我们接受您的捐赠/感谢!</translation>
-    </message>
-    <message>
-        <source>Hedgewars is Open Source and Freeware we create in our spare time. Share it with your family and friends as you like!</source>
-        <comment>Tips</comment>
-        <translation>刺猬大作战是一个开放源代码的免费软件,它充分利用了我们的业余时间。与他人分享它吧!</translation>
-    </message>
-    <message>
-        <source>From time to time there will be official tournaments. Upcoming events will be announced at http://www.hedgewars.org/ some days in advance.</source>
-        <comment>Tips</comment>
-        <translation>官方的竞赛一直存在。临近时去  http://www.hedgewars.org/  即可看到。</translation>
-    </message>
-    <message>
-        <source>Hedgewars is available in many languages. If the translation in your language seems to be missing or outdated, feel free to contact us!</source>
-        <comment>Tips</comment>
-        <translation>Hedgewars 被翻译成多种语言,中文是刺猬大作战——同样为翻译名。如果您的语言翻译有什么缺失/过时/遗漏或任何问题,来联系我们吧!</translation>
-    </message>
-    <message>
-        <source>Hedgewars can be run on lots of different operating systems including Microsoft Windows, Mac OS X and Linux.</source>
-        <comment>Tips</comment>
-        <translation>刺猬大作战可以运行的操作系统包括:GNU/Linux、Mac OS X、MicroSoft Windows。</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>Multiplayer</source>
+        <translation type="obsolete">多人游戏</translation>
     </message>
     <message>
-        <source>Always remember you&apos;re able to set up your own games in local and network/online play. You&apos;re not restricted to the &apos;Simple Game&apos; option.</source>
-        <comment>Tips</comment>
-        <translation>您可以建立自己的网络游戏/局域网游戏。不仅限于 &quot;简单游戏&quot; 选项。</translation>
-    </message>
-    <message>
-        <source>Create an account on http://www.hedgewars.org/ to keep others from using your most favourite nickname while playing on the official server.</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">在官方服务器 http://www.hedgewars.org/ 建立自己的帐号——就能一直使用最喜欢的昵称</translation>
-    </message>
-    <message>
-        <source>While playing you should give yourself a short break at least once an hour.</source>
-        <comment>Tips</comment>
-        <translation>最好玩一个小时就休息一下,如果你要继续用电脑。</translation>
-    </message>
-    <message>
-        <source>If your graphics card isn&apos;t able to provide hardware accelerated OpenGL, try to enable the low quality mode to improve performance.</source>
-        <comment>Tips</comment>
-        <translation>假如你的显卡不能提供OpenGL硬件加速,试着用降低效果的方式运行。</translation>
-    </message>
-    <message>
-        <source>We&apos;re open to suggestions and constructive feedback. If you don&apos;t like something or got a great idea, let us know!</source>
-        <comment>Tips</comment>
-        <translation>我们接受意见和建设性反馈。假如您有好电子或者不喜欢的东西,告诉我们!</translation>
-    </message>
-    <message>
-        <source>Especially while playing online be polite and always remember there might be some minors playing with or against you as well!</source>
-        <comment>Tips</comment>
-        <translation>特别是网络游戏,请有礼貌记住对方也和您一样是人!</translation>
-    </message>
-    <message>
-        <source>Special game modes such as &apos;Vampirism&apos; or &apos;Karma&apos; allow you to develop completely new tactics. Try them in a custom game!</source>
-        <comment>Tips</comment>
-        <translation>特别游戏模式“吸血“、”因果报应“需要全新的战术。现在自定义游戏里试试!</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>Single Player</source>
+        <translation type="obsolete">单人游戏</translation>
     </message>
     <message>
-        <source>The Windows version of Hedgewars supports Xfire. Make sure to add Hedgwars to its game list so your friends can see you playing.</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">Windows版本的刺猬大作战支持Xfire。添加它到游戏列表里让您的朋友看到。</translation>
-    </message>
-    <message>
-        <source>You should never install Hedgewars on computers you don&apos;t own (school, university, work, etc.). Please ask the responsible person instead!</source>
-        <comment>Tips</comment>
-        <translation>您不应该在不属于您的计算机上安装刺猬大作战——比如学校/工作场所等。您应当向计算机的负责人咨询!</translation>
-    </message>
-    <message>
-        <source>Hedgewars can be perfect for short games during breaks. Just ensure you don&apos;t add too many hedgehogs or use an huge map. Reducing time and health might help as well.</source>
-        <comment>Tips</comment>
-        <translation>刺猬大作战适合短时间休息,不需要太多刺猬挤在大地图上。</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>Net game</source>
+        <translation type="obsolete">网络游戏</translation>
     </message>
     <message>
-        <source>No hedgehogs were harmed in making this game.</source>
-        <comment>Tips</comment>
-        <translation>在制作这款游戏时没有任何刺猬受到伤害。</translation>
-    </message>
-    <message>
-        <source>Connect one or more gamepads before launching the game to be able to assign their controls to your teams.</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">在运行游戏前连接游戏板</translation>
-    </message>
-    <message>
-        <source>Hedgewars is Open Source and Freeware we create in our spare time. If someone sold you the game, you should try get a refund!</source>
-        <comment>Tips</comment>
-        <translation>刺猬大作战是开放源代码的免费软件,用我们的闲暇时间创造。如果有人卖给你,那么应该把钱拿回来!</translation>
-    </message>
-    <message>
-        <source>Create an account on %1 to keep others from using your most favourite nickname while playing on the official server.</source>
-        <comment>Tips</comment>
-        <translation>在 %1 建立一个帐号阻止其他人使用你喜欢的名称在官方服务器游戏。</translation>
-    </message>
-    <message>
-        <source>There are three different jumps available. Tap [high jump] twice to do a very high/backwards jump.</source>
-        <comment>Tips</comment>
-        <translation>三种跳跃方式。点击[高跳]两次做出跳跃高度极限的后空翻。</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>Saved games</source>
+        <translation type="obsolete">存档</translation>
     </message>
     <message>
-        <source>Afraid of falling off a cliff? Hold down [precise] to turn [left] or [right] without actually moving.</source>
-        <comment>Tips</comment>
-        <translation>害怕掉下悬崖?按住[精确]后再点击[左][右]就会只转身,不移动位置。</translation>
-    </message>
-    <message>
-        <source>Some weapons require special strategies or just lots of training, so don&apos;t give up on a particular tool if you miss an enemy once.</source>
-        <comment>Tips</comment>
-        <translation>有些武器需要特殊策略或者仅仅是大量的练习,假如你一次失去准星,也不要放弃。</translation>
-    </message>
-    <message>
-        <source>Most weapons won&apos;t work once they touch the water. The Homing Bee as well as the Cake are exceptions to this.</source>
-        <comment>Tips</comment>
-        <translation>多数武器不会在接触水之后生效。归巢的蜜蜂和蛋糕是例外。</translation>
-    </message>
-    <message>
-        <source>The Old Limbuger only causes a small explosion. However the wind affected smelly cloud can poison lots of hogs at once.</source>
-        <comment>Tips</comment>
-        <translation>老干酪发射器造成小规模爆炸,然后产生随风移动的有毒云雾——能一次毒害多个刺猬。</translation>
-    </message>
-    <message>
-        <source>The Piano Strike is the most damaging air strike. You&apos;ll lose the hedgehog performing it, so there&apos;s a huge downside as well.</source>
-        <comment>Tips</comment>
-        <translation>钢琴攻击是最大威力的空袭。弹奏钢琴的刺猬会跟着钢琴返回天堂。</translation>
-    </message>
-    <message>
-        <source>The Homing Bee can be tricky to use. It&apos;s turn radius depends on it&apos;s velocity, so try to not use full power.</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">归巢的蜜蜂有些技巧。它的回转半径和初速有关,最好不用全力发射。</translation>
-    </message>
-    <message>
-        <source>Sticky Mines are a perfect tool to create small chain reactions knocking enemy hedgehogs into dire situations ... or water.</source>
-        <comment>Tips</comment>
-        <translation>黏着地雷是创造小范围连锁反应的绝佳武器。</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>Demos</source>
+        <translation type="obsolete">Demo</translation>
     </message>
     <message>
-        <source>The Hammer is most effective when used on bridges or girders. Hit hogs will just break through the ground.</source>
-        <comment>Tips</comment>
-        <translation>锤是桥梁上/分界处最佳武器之一,只是刚刚好把刺猬打陷——如果没底就没办法了。</translation>
-    </message>
-    <message>
-        <source>If you&apos;re stuck behind an enemy hedgehog, use the Hammer to free yourself without getting damaged by an explosion.</source>
-        <comment>Tips</comment>
-        <translation>假如对方刺猬把你堵住了,一锤打下去让自己轻松些。</translation>
-    </message>
-    <message>
-        <source>The Cake&apos;s maximum walking distance depends on the ground it has to pass. Use [attack] to detonate it early.</source>
-        <comment>Tips</comment>
-        <translation>蛋糕的最大行走距离取决于地表。也可按下[攻击键]激活起爆。</translation>
-    </message>
-    <message>
-        <source>The Flame Thrower is a weapon but it can be used for tunnel digging as well.</source>
-        <comment>Tips</comment>
-        <translation>火焰喷射器是一种武器,也是一种开路机器。</translation>
-    </message>
-    <message>
-        <source>Use the Incinerating Grenade to temporary keep hedgehogs from passing terrain such as tunnels or platforms.</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">燃烧瓶可以短时阻止刺猬通过特定区域(比如通道或平台)</translation>
-    </message>
-    <message>
-        <source>Want to know who&apos;s behind the game? Click on the Hedgewars logo in the main menu to see the credits.</source>
-        <comment>Tips</comment>
-        <translation>想要知道谁是此游戏的幕后人员?点击主菜单的Hedgewars Logo就可以看到贡献者名单。</translation>
-    </message>
-    <message>
-        <source>Like hedgewars? Become a fan on %1 or join our group at %2. You could follow us on %3 as well!</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">喜欢刺猬大作战(hedgewars)?那么加入我们 %1 或者 %2.。你可以在 %3 跟随我们!</translation>
-    </message>
-    <message>
-        <source>You can find your Hedgewars configuration files under &quot;My Documents\Hedgewars&quot;. Create backups or take the files with you, but don&apos;t edit them by hand.</source>
-        <comment>Tips</comment>
-        <translation>你可以在( 我的文档\Hedgewars)里找到设置文件。可以创建备份,但不要手动修改。</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>Setup</source>
+        <translation type="obsolete">设置</translation>
     </message>
     <message>
-        <source>You can find your Hedgewars configuration files under &quot;Hedgewars&quot; in your home directory. Create backups or take the files with you, but don&apos;t edit them by hand.</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">你可以在家目录找到 .hedgewars。可以创建备份,但不要手动修改。</translation>
-    </message>
-    <message>
-        <source>Connect one or more gamepads before starting the game to be able to assign their controls to your teams.</source>
-        <comment>Tips</comment>
-        <translation>在游戏开始前连接游戏手柄才能用它们操控你的队伍。</translation>
-    </message>
-    <message>
-        <source>If your graphics card isn&apos;t able to provide hardware accelerated OpenGL, try to update the associated drivers.</source>
-        <comment>Tips</comment>
-        <translation>加入你的显卡不能使用OpenGL硬件加速,请升级相应驱动。</translation>
-    </message>
-    <message>
-        <source>Like Hedgewars? Become a fan on %1 or join our group at %2. You could follow us on %3 as well!</source>
-        <comment>Tips</comment>
-        <translation type="obsolete">喜欢刺猬大作战(Hedgewars)吗?加入 %2 ,成为 %1 粉丝,也可以在  %3 跟随我们!</translation>
-    </message>
-    <message>
-        <source>Feel free to draw your own graves, hats, flags or even maps and themes! But note that you&apos;ll have to share them somewhere to use them online.</source>
-        <comment>Tips</comment>
-        <translation>欢迎你自己绘制墓碑,帽子(头饰),旗帜或者地图,主题!但是记住,如果要在网上使用,你的必须把它们分享出来。</translation>
-    </message>
-    <message>
-        <source>Really want to wear a specific hat? Donate to us and receive an exclusive hat of your choice!</source>
-        <comment>Tips</comment>
-        <translation>非常想要一个帽子?捐赠的话就给你!</translation>
-    </message>
-    <message>
-        <source>Keep your video card drivers up to date to avoid issues playing the game.</source>
-        <comment>Tips</comment>
-        <translation>保持显卡驱动最新避免可能的麻烦。</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>About</source>
+        <translation type="obsolete">关于</translation>
     </message>
     <message>
-        <source>You&apos;re able to associate Hedgewars related files (savegames and demo recordings) with the game to launch them right from your favorite file or internet browser.</source>
-        <comment>Tips</comment>
-        <translation>你可以关联刺猬大作战的相关文件(比如存档和回放)到本游戏以便在网络或文件浏览器中直接打开这些文件。</translation>
-    </message>
-    <message>
-        <source>Want to save ropes? Release the rope in mid air and then shoot again. As long as you don&apos;t touch the ground you&apos;ll reuse your rope without wasting ammo!</source>
-        <comment>Tips</comment>
-        <translation>想要节省绳子?放开绳子之后再次发射,只要你不曾脱离绳子接触接触地面就可以继续使用同一根!</translation>
-    </message>
-    <message>
-        <source>Like Hedgewars? Become a fan on %1 or follow us on %2!</source>
-        <comment>Tips</comment>
-        <translation>喜欢刺猬大作战Hedgewars吗?来 %1 或者 %2 追随我们吧!</translation>
-    </message>
-    <message>
-        <source>You can find your Hedgewars configuration files under &quot;Library/Application Support/Hedgewars&quot; in your home directory. Create backups or take the files with you, but don&apos;t edit them by hand.</source>
-        <comment>Tips</comment>
-        <translation>在家目录的&quot;Library/Application Support/Hedgewars&quot;找到刺猬的配置文件。备份随你,但是不要手动编辑。</translation>
+        <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+        <source>Exit</source>
+        <translation type="obsolete">退出</translation>
     </message>
     <message>
-        <source>You can find your Hedgewars configuration files under &quot;.hedgewars&quot; in your home directory. Create backups or take the files with you, but don&apos;t edit them by hand.</source>
-        <comment>Tips</comment>
-        <translation>在家目录的&quot;.hedgewars&quot;找到刺猬的配置文件。备份随你,但是不要手动编辑。</translation>
-    </message>
-    <message>
-        <source>The Windows version of Hedgewars supports Xfire. Make sure to add Hedgewars to its game list so your friends can see you playing.</source>
-        <comment>Tips</comment>
-        <translation>Windows版本的刺猬大作战支持Xfire。您可以添加刺猬大作战到它的游戏列表里让您的朋友看到。</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="81"/>
+        <source>Local Game (Play a game on a single computer)</source>
+        <translation>本地游戏(在一台电脑上)</translation>
     </message>
     <message>
-        <source>Use the Molotov or Flame Thrower to temporary keep hedgehogs from passing terrain such as tunnels or platforms.</source>
-        <comment>Tips</comment>
-        <translation>使用燃烧瓶或火焰喷射器可以短时阻止刺猬通过特定区域(比如隧道或平台).</translation>
-    </message>
-    <message>
-        <source>The Homing Bee can be tricky to use. Its turn radius depends on its velocity, so try to not use full power.</source>
-        <comment>Tips</comment>
-        <translation>使用归巢的蜜蜂有些技巧,它的回转半径和发射速度有关,所以最好不要全力发射。</translation>
-    </message>
-    <message>
-        <source>Downloadable Content</source>
-        <translation>可下载内容</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="85"/>
+        <source>Network Game (Play a game across a network)</source>
+        <translation>网络游戏(通过网络)</translation>
     </message>
 </context>
 <context>
     <name>PageMultiplayer</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Back</source>
+        <translation type="obsolete">返回</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="309"/>
         <source>Start</source>
         <translation>开始</translation>
     </message>
@@ -900,40 +441,43 @@
 <context>
     <name>PageNet</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Local</source>
+        <translation type="obsolete">本地</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Internet</source>
+        <translation type="obsolete">Internet</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="520"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
-        <source>Please select server from the list above</source>
+        <location filename="../../../../QTfrontend/pages.cpp" line="520"/>
+        <source>Please, select server from the list above</source>
         <translation>请选择一个服务器</translation>
     </message>
 </context>
 <context>
     <name>PageNetGame</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="626"/>
         <source>Control</source>
-        <translation>房间管理</translation>
-    </message>
-    <message>
-        <source>Error</source>
-        <translation>错误</translation>
-    </message>
-    <message>
-        <source>Please enter room name</source>
-        <translation>请键入房间名</translation>
-    </message>
-    <message>
-        <source>OK</source>
-        <translation>确定</translation>
+        <translation>Ctrl</translation>
     </message>
 </context>
 <context>
     <name>PageNetType</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="1097"/>
         <source>LAN game</source>
         <translation>局域网游戏</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="1098"/>
         <source>Official server</source>
         <translation>官方服务器</translation>
     </message>
@@ -941,925 +485,681 @@
 <context>
     <name>PageOptions</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="344"/>
         <source>New team</source>
         <translation>新队伍</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="345"/>
         <source>Edit team</source>
         <translation>修改队伍设定</translation>
     </message>
     <message>
-        <source>Delete team</source>
-        <translation>删除队伍</translation>
-    </message>
-    <message>
-        <source>New weapon scheme</source>
-        <translation type="obsolete">新武器配置</translation>
-    </message>
-    <message>
-        <source>Edit weapon scheme</source>
-        <translation type="obsolete">修改武器配置</translation>
-    </message>
-    <message>
-        <source>Delete weapon scheme</source>
-        <translation type="obsolete">删除武器配置</translation>
-    </message>
-    <message>
-        <source>You can&apos;t edit teams from team selection. Go back to main menu to add, edit or delete teams.</source>
-        <translation>您不能在队伍选择界面修改队伍。请返回主页面进行添加、修改、删除队伍等操作。</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Save</source>
+        <translation type="obsolete">保存</translation>
     </message>
     <message>
-        <source>New scheme</source>
-        <translation>新框架</translation>
-    </message>
-    <message>
-        <source>Edit scheme</source>
-        <translation>修改框架</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Back</source>
+        <translation type="obsolete">返回</translation>
     </message>
     <message>
-        <source>Delete scheme</source>
-        <translation>删除框架</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="373"/>
+        <source>Weapons set</source>
+        <translation>新武器设定</translation>
     </message>
     <message>
-        <source>New weapon set</source>
-        <translation>新武器配置</translation>
-    </message>
-    <message>
-        <source>Edit weapon set</source>
-        <translation>修改武器配置</translation>
-    </message>
-    <message>
-        <source>Delete weapon set</source>
-        <translation>删除武器配置</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="376"/>
+        <source>Edit</source>
+        <translation>修改当前武器设定</translation>
     </message>
 </context>
 <context>
     <name>PagePlayDemo</name>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="149"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="137"/>
+        <source>Please, select record from the list</source>
+        <translation>请从列表选择记录</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="138"/>
         <source>OK</source>
         <translation>确认</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="113"/>
         <source>Rename dialog</source>
         <translation>重命名对话框</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="113"/>
         <source>Enter new file name:</source>
         <translation>输入新的文件名:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="124"/>
         <source>Cannot rename to</source>
         <translation>不能改变名字</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="149"/>
         <source>Cannot delete file</source>
         <translation>不能删除文件</translation>
     </message>
-    <message>
-        <source>Please select record from the list</source>
-        <translation>请从列表选择记录</translation>
-    </message>
 </context>
 <context>
     <name>PageRoomsList</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="764"/>
         <source>Create</source>
         <translation>建立</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="765"/>
         <source>Join</source>
         <translation>加入</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="766"/>
         <source>Refresh</source>
         <translation>刷新</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="828"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="818"/>
+        <source>Please, enter room name</source>
+        <translation>请键入房间名</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="830"/>
         <source>OK</source>
         <translation>确认</translation>
     </message>
     <message>
-        <source>Admin features</source>
-        <translation>管理员功能</translation>
-    </message>
-    <message>
-        <source>Room Name:</source>
-        <translation>房间名:</translation>
-    </message>
-    <message>
-        <source>This game is in lobby.
-You may join and start playing once the game starts.</source>
-        <translation>游戏正在大厅中。
-您可以加入等待游戏开始。</translation>
-    </message>
-    <message>
-        <source>This game is in progress.
-You may join and spectate now but you&apos;ll have to wait for the game to end to start playing.</source>
-        <translation>游戏正在进行中。
-您可以加入观战但必须等游戏结束才能参与游戏。</translation>
-    </message>
-    <message>
-        <source>%1 is the host. He may adjust settings and start the game.</source>
-        <translation>%1 是房主,他可以调整设置、开始游戏。</translation>
-    </message>
-    <message>
-        <source>Random Map</source>
-        <translation>随机地图</translation>
-    </message>
-    <message>
-        <source>Games may be played on precreated or randomized maps.</source>
-        <translation>游戏可以在预先创建或者随机产生的地图上进行。</translation>
-    </message>
-    <message>
-        <source>The Game Scheme defines general options and preferences like Round Time, Sudden Death or Vampirism.</source>
-        <translation>游戏设置包括一般选项例如回合时间,突然死亡或吸血模式。</translation>
-    </message>
-    <message>
-        <source>The Weapon Scheme defines available weapons and their ammunition count.</source>
-        <translation>武器配置包括可以选用的武器和弹药数量。</translation>
-    </message>
-    <message numerus="yes">
-        <source>There are %1 clients connected to this room.</source>
-        <translation>
-            <numerusform>有 %1 个客户端连接到这个房间。</numerusform>
-        </translation>
-    </message>
-    <message numerus="yes">
-        <source>There are %1 teams participating in this room.</source>
-        <translation>
-            <numerusform>有 %1 个队伍加入这个房间。</numerusform>
-        </translation>
-    </message>
-    <message>
-        <source>Please enter room name</source>
-        <translation>请键入房间名</translation>
-    </message>
-    <message>
-        <source>Please select room from the list</source>
+        <location filename="../../../../QTfrontend/pages.cpp" line="829"/>
+        <source>Please, select room from the list</source>
         <translation>请从列表选中房间</translation>
     </message>
     <message>
-        <source>Random Maze</source>
-        <translation>随机迷宫</translation>
-    </message>
-    <message>
-        <source>State:</source>
-        <translation type="obsolete">游戏状态</translation>
-    </message>
-    <message>
-        <source>Rules:</source>
-        <translation>规则</translation>
-    </message>
-    <message>
-        <source>Weapons:</source>
-        <translation>武器</translation>
-    </message>
-    <message>
-        <source>Search:</source>
-        <translation>搜索</translation>
-    </message>
-    <message>
-        <source>Clear</source>
-        <translation>清除</translation>
-    </message>
-    <message>
-        <source>Warning</source>
-        <translation>警告</translation>
-    </message>
-    <message>
-        <source>The game you are trying to join has started.
-Do you still want to join the room?</source>
-        <translation>你要加入的游戏已经开始了。
-还要进入房间吗?</translation>
-    </message>
-    <message numerus="yes">
-        <source>%1 players online</source>
-        <translation>
-            <numerusform>%1 个玩家在线</numerusform>
-        </translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="769"/>
+        <source>Admin features</source>
+        <translation>管理员功能</translation>
     </message>
 </context>
 <context>
     <name>PageScheme</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="1008"/>
         <source>New</source>
-        <translation>新模式</translation>
-    </message>
-    <message>
-        <source>Delete</source>
-        <translation>删除</translation>
-    </message>
-    <message>
-        <source>Defend your fort and destroy the opponents, two team colours max!</source>
-        <translation>保卫你的城堡,破坏对手的,努力吧!</translation>
-    </message>
-    <message>
-        <source>Teams will start on opposite sides of the terrain, two team colours max!</source>
-        <translation>队伍开始在对手的地盘,努力!</translation>
-    </message>
-    <message>
-        <source>Land can not be destroyed!</source>
-        <translation>地面无法破坏!</translation>
-    </message>
-    <message>
-        <source>Add an indestructable border around the terrain</source>
-        <translation type="obsolete">添加不可毁坏地边界</translation>
-    </message>
-    <message>
-        <source>Lower gravity</source>
-        <translation>低重力</translation>
-    </message>
-    <message>
-        <source>Assisted aiming with laser sight</source>
-        <translation>激光瞄准辅助</translation>
-    </message>
-    <message>
-        <source>All hogs have a personal forcefield</source>
-        <translation>每个刺猬都有一个力场</translation>
-    </message>
-    <message>
-        <source>Enable random mines</source>
-        <translation type="obsolete">开启随机地雷</translation>
-    </message>
-    <message>
-        <source>Gain 80% of the damage you do back in health</source>
-        <translation>伤害的80%变成自身力量</translation>
-    </message>
-    <message>
-        <source>Share your opponents pain, share their damage</source>
-        <translation>分担你的对手的疼痛</translation>
-    </message>
-    <message>
-        <source>Your hogs are unable to move, put your artillery skills to the test</source>
-        <translation>你的刺猬不能移动,检验你射击技巧的时候到了</translation>
-    </message>
-    <message>
-        <source>Random</source>
-        <translation>随机</translation>
-    </message>
-    <message>
-        <source>Seconds</source>
-        <translation>秒钟</translation>
+        <translation>新游戏</translation>
     </message>
     <message>
-        <source>Order of play is random instead of in room order.</source>
-        <translation>出场顺序是随机的而不是按照房间顺序。</translation>
-    </message>
-    <message>
-        <source>Play with a King. If he dies, your side dies.</source>
-        <translation>国王不能死。</translation>
-    </message>
-    <message>
-        <source>Take turns placing your hedgehogs before the start of play.</source>
-        <translation>在开局前轮流手动放置刺猬。</translation>
-    </message>
-    <message>
-        <source>Ammo is shared between all teams that share a colour.</source>
-        <translation>同色队伍共享所有弹药。</translation>
-    </message>
-    <message>
-        <source>Disable girders when generating random maps.</source>
-        <translation>禁止随机生成地图时使用梁。</translation>
-    </message>
-    <message>
-        <source>Disable land objects when generating random maps.</source>
-        <translation type="unfinished">禁止随机生成地图时使用地面物体。</translation>
-    </message>
-    <message>
-        <source>AI respawns on death.</source>
-        <translation>AI 死亡再生。</translation>
-    </message>
-    <message>
-        <source>Attacking does not end your turn.</source>
-        <translation>攻击不会结束当前回合。</translation>
-    </message>
-    <message>
-        <source>Weapons are reset to starting values each turn.</source>
-        <translation>在每回合武器将自动重置到开始设定。</translation>
-    </message>
-    <message>
-        <source>Each hedgehog has its own ammo. It does not share with the team.</source>
-        <translation>每个刺猬都有独立的弹药,而非团队分享。</translation>
-    </message>
-    <message>
-        <source>All (living) hedgehogs are fully restored at the end of turn</source>
-        <translation>所有活着的刺猬在回合结束时完全恢复健康</translation>
-    </message>
-    <message>
-        <source>You will not have to worry about wind anymore.</source>
-        <translation>不用担心风的影响了。</translation>
-    </message>
-    <message>
-        <source>Wind will affect almost everything.</source>
-        <translation>风无所不在。</translation>
-    </message>
-    <message>
-        <source>Copy</source>
-        <translation>备份</translation>
-    </message>
-    <message>
-        <source>Teams in each clan take successive turns sharing their turn time.</source>
-        <translation>在同一集团中的队伍在共用的回合时间里使用连续的回合。</translation>
-    </message>
-    <message>
-        <source>Add an indestructible border around the terrain</source>
-        <translation>添加不可毁坏地边界</translation>
-    </message>
-    <message>
-        <source>Add an indestructible border along the bottom</source>
-        <translation>在底部添加一个不可毁坏的边界</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="1009"/>
+        <source>Delete</source>
+        <translation>删除</translation>
     </message>
 </context>
 <context>
     <name>PageSelectWeapon</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Back</source>
+        <translation type="obsolete">返回</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="729"/>
         <source>Default</source>
         <translation>默认</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="730"/>
         <source>Delete</source>
         <translation>删除</translation>
     </message>
     <message>
-        <source>New</source>
-        <translation>新模式</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Save</source>
+        <translation type="obsolete">保存</translation>
+    </message>
+</context>
+<context>
+    <name>PageSimpleGame</name>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Back</source>
+        <translation type="obsolete">返回</translation>
     </message>
     <message>
-        <source>Copy</source>
-        <translation>备份</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Simple Game</source>
+        <translation type="obsolete">简单游戏</translation>
     </message>
 </context>
 <context>
     <name>PageSinglePlayer</name>
     <message>
-        <source>Simple Game (a quick game against the computer, settings are chosen for you)</source>
-        <translation>快速游戏 (使用预设对抗电脑)</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Simple Game</source>
+        <translation type="obsolete">简单游戏</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Training</source>
+        <translation type="obsolete">训练</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Multiplayer</source>
+        <translation type="obsolete">多人游戏</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Saved games</source>
+        <translation type="obsolete">游戏存档</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Demos</source>
+        <translation type="obsolete">Demo</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="685"/>
+        <source>Simple Game (a quick game against the computer, settings are chosen for you)</source>
+        <translation>快速游戏 (对抗电脑,固定设置)</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="688"/>
         <source>Multiplayer (play a hotseat game against your friends, or AI teams)</source>
         <translation>多人游戏 (热坐对抗朋友或AI)</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="693"/>
         <source>Training Mode (Practice your skills in a range of training missions). IN DEVELOPMENT</source>
-        <translation type="obsolete">训练模式 (一系列训练任务)。开发中</translation>
+        <translation>训练模式 (一系列训练任务)。开发中</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="699"/>
         <source>Demos (Watch recorded demos)</source>
         <translation>Demo (观看记录的Demo)</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="702"/>
         <source>Load (Load a previously saved game)</source>
         <translation>读取 (读取之前保存的游戏)</translation>
     </message>
-    <message>
-        <source>Campaign Mode (...). IN DEVELOPMENT</source>
-        <translation type="obsolete">战役模式 ——开发中</translation>
-    </message>
-    <message>
-        <source>Campaign Mode (...)</source>
-        <translation>战役模式 (...)</translation>
-    </message>
-    <message>
-        <source>Training Mode (Practice your skills in a range of training missions)</source>
-        <translation>训练模式(在一系列训练任务中练习你的技能)</translation>
-    </message>
-</context>
-<context>
-    <name>PageTraining</name>
-    <message>
-        <source>No description available</source>
-        <translation>没有可用描述</translation>
-    </message>
-    <message>
-        <source>Select a mission!</source>
-        <translation>选择一个任务!</translation>
-    </message>
 </context>
 <context>
     <name>QAction</name>
     <message>
+        <location filename="../../../../QTfrontend/chatwidget.cpp" line="62"/>
         <source>Kick</source>
-        <translation>踢出</translation>
+        <translation>踢</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="638"/>
         <source>Start</source>
         <translation>开始</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="628"/>
         <source>Restrict Joins</source>
         <translation>限制参与</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="630"/>
         <source>Restrict Team Additions</source>
-        <translation>限制增加团队</translation>
+        <translation>限制团队插件</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/chatwidget.cpp" line="60"/>
         <source>Info</source>
         <translation>信息</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/chatwidget.cpp" line="64"/>
         <source>Ban</source>
         <translation>屏蔽</translation>
     </message>
-    <message>
-        <source>Follow</source>
-        <translation>跟随</translation>
-    </message>
-    <message>
-        <source>Ignore</source>
-        <translation>忽略</translation>
-    </message>
-    <message>
-        <source>Add friend</source>
-        <translation>添加朋友</translation>
-    </message>
-    <message>
-        <source>Unignore</source>
-        <translation>取消忽略</translation>
-    </message>
-    <message>
-        <source>Remove friend</source>
-        <translation>移除朋友</translation>
-    </message>
-    <message>
-        <source>Update</source>
-        <translation>更新</translation>
-    </message>
 </context>
 <context>
     <name>QCheckBox</name>
     <message>
-        <source>Check for updates at startup</source>
-        <translation>启动时检查程序升级</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="415"/>
+        <source>Enable sound</source>
+        <translation>开启音效</translation>
     </message>
     <message>
-        <source>Enable sound</source>
-        <translation>开启游戏音效</translation>
-    </message>
-    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="407"/>
         <source>Fullscreen</source>
         <translation>游戏全屏幕</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="861"/>
+        <source>Forts mode</source>
+        <translation type="obsolete">城堡模式</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="434"/>
         <source>Show FPS</source>
         <translation>显示帧率 (FPS)</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="438"/>
         <source>Alternative damage show</source>
         <translation>另一种伤害显示方式</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="419"/>
         <source>Enable music</source>
-        <translation>开启游戏音乐</translation>
+        <translation>开启音乐</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="411"/>
         <source>Frontend fullscreen</source>
-        <translation>前端界面全屏幕</translation>
-    </message>
-    <message>
-        <source>Append date and time to record file name</source>
-        <translation>记录名称中包含具体时间和日期</translation>
+        <translation>界面全屏幕</translation>
     </message>
     <message>
-        <source>Reduced quality</source>
-        <translation type="obsolete">降低显示效果</translation>
-    </message>
-    <message>
-        <source>Show ammo menu tooltips</source>
-        <translation>显示武器菜单提示</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="865"/>
+        <source>Divide teams</source>
+        <translation type="obsolete">分组</translation>
     </message>
     <message>
-        <source>Enable frontend sounds</source>
-        <translation>开启前端界面音效</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="442"/>
+        <source>Append date and time to record file name</source>
+        <translation>记录名称中包含具体时间日期</translation>
     </message>
     <message>
-        <source>Enable frontend music</source>
-        <translation>开启前端界面音乐</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="869"/>
+        <source>Solid land</source>
+        <translation type="obsolete">固实地面</translation>
     </message>
     <message>
-        <source>Frontend effects</source>
-        <translation>前端界面效果</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="403"/>
+        <source>Reduce Quality</source>
+        <translation>降低质量</translation>
     </message>
 </context>
 <context>
     <name>QComboBox</name>
     <message>
+        <location filename="../../../../QTfrontend/mapContainer.cpp" line="62"/>
         <source>generated map...</source>
         <translation>生成地图...</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="171"/>
         <source>Human</source>
         <translation>玩家</translation>
     </message>
     <message>
-        <source>Level</source>
-        <translation>Lv 级别</translation>
-    </message>
-    <message>
-        <source>(System default)</source>
-        <translation>(系统默认)</translation>
-    </message>
-    <message>
-        <source>generated maze...</source>
-        <translation>生成迷宫...</translation>
-    </message>
-    <message>
-        <source>Mission</source>
-        <translation>任务</translation>
-    </message>
-    <message>
-        <source>Community</source>
-        <translation>社区</translation>
-    </message>
-    <message>
-        <source>Any</source>
-        <translation>任意</translation>
-    </message>
-    <message>
-        <source>In lobby</source>
-        <translation>大厅中</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Level 5</source>
+        <translation type="obsolete">Lv 5</translation>
     </message>
     <message>
-        <source>In progress</source>
-        <translation>进行中</translation>
-    </message>
-    <message>
-        <source>Default</source>
-        <translation type="obsolete">默认</translation>
-    </message>
-    <message>
-        <source>Pro mode</source>
-        <translation type="obsolete">高手模式</translation>
-    </message>
-    <message>
-        <source>Shoppa</source>
-        <translation type="obsolete">绳子党</translation>
-    </message>
-    <message>
-        <source>Basketball</source>
-        <translation type="obsolete">篮球</translation>
-    </message>
-    <message>
-        <source>Minefield</source>
-        <translation type="obsolete">雷区</translation>
-    </message>
-    <message>
-        <source>Barrel mayhem</source>
-        <translation type="obsolete">炼狱场</translation>
-    </message>
-    <message>
-        <source>Tunnel hogs</source>
-        <translation type="obsolete">刺猬洞</translation>
-    </message>
-    <message>
-        <source>Crazy</source>
-        <translation type="obsolete">疯狂刺猬</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Level 4</source>
+        <translation type="obsolete">Lv 4</translation>
     </message>
     <message>
-        <source>hand drawn map...</source>
-        <translation>手绘地图...</translation>
-    </message>
-    <message>
-        <source>Disabled</source>
-        <translation>禁用</translation>
-    </message>
-    <message>
-        <source>Red/Cyan</source>
-        <translation>红/青</translation>
-    </message>
-    <message>
-        <source>Cyan/Red</source>
-        <translation>青/红</translation>
-    </message>
-    <message>
-        <source>Red/Blue</source>
-        <translation>红/蓝</translation>
-    </message>
-    <message>
-        <source>Blue/Red</source>
-        <translation>蓝/红</translation>
-    </message>
-    <message>
-        <source>Red/Green</source>
-        <translation>红/绿</translation>
-    </message>
-    <message>
-        <source>Green/Red</source>
-        <translation>绿/红</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Level 3</source>
+        <translation type="obsolete">Lv 3</translation>
     </message>
     <message>
-        <source>Side-by-side</source>
-        <translation>横向排列</translation>
-    </message>
-    <message>
-        <source>Top-Bottom</source>
-        <translation>自顶向下</translation>
-    </message>
-    <message>
-        <source>Wiggle</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <source>Red/Cyan grayscale</source>
-        <translation type="unfinished">红/青 灰度</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Level 2</source>
+        <translation type="obsolete">Lv 2</translation>
     </message>
     <message>
-        <source>Cyan/Red grayscale</source>
-        <translation type="unfinished">青/红 灰度</translation>
-    </message>
-    <message>
-        <source>Red/Blue grayscale</source>
-        <translation type="unfinished">红/蓝 灰度</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Level 1</source>
+        <translation type="obsolete">Lv 1</translation>
     </message>
     <message>
-        <source>Blue/Red grayscale</source>
-        <translation type="unfinished">蓝/红 灰度</translation>
-    </message>
-    <message>
-        <source>Red/Green grayscale</source>
-        <translation type="unfinished">红/绿 灰度</translation>
-    </message>
-    <message>
-        <source>Green/Red grayscale</source>
-        <translation type="unfinished">绿/红 灰度</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="175"/>
+        <source>Level</source>
+        <translation>Lv 级别</translation>
     </message>
 </context>
 <context>
     <name>QGroupBox</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="126"/>
         <source>Team Members</source>
         <translation>成员</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="161"/>
+        <source>Team</source>
+        <translation>队伍</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="202"/>
         <source>Fort</source>
         <translation>城堡模式</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="236"/>
         <source>Key binds</source>
         <translation>键位绑定</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="335"/>
         <source>Teams</source>
         <translation>队伍</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="384"/>
         <source>Audio/Graphic options</source>
         <translation>音频/视频选项</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Net nick</source>
+        <translation type="obsolete">昵称</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Net options</source>
+        <translation type="obsolete">网络选项</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Landscape</source>
+        <translation type="obsolete">地形</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Game scheme</source>
+        <translation type="obsolete">游戏设置</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="228"/>
         <source>Playing teams</source>
         <translation>玩家队伍</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+        <source>Team level</source>
+        <translation type="obsolete">队伍级别</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="473"/>
         <source>Net game</source>
-        <translation>局域网络游戏</translation>
+        <translation>网络游戏</translation>
     </message>
     <message>
-        <source>Weapons</source>
-        <translation type="obsolete">武器</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Servers list</source>
+        <translation type="obsolete">服务器列表</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="370"/>
+        <source>Weapons</source>
+        <translation>武器</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="862"/>
+        <source>Scheme options</source>
+        <translation type="obsolete">游戏设定</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="866"/>
         <source>Game Modifiers</source>
         <translation>游戏修改</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="867"/>
         <source>Basic Settings</source>
         <translation>基本设置</translation>
     </message>
-    <message>
-        <source>Team Settings</source>
-        <translation>队伍设定</translation>
-    </message>
-    <message>
-        <source>Misc</source>
-        <translation>杂项</translation>
-    </message>
-    <message>
-        <source>Schemes and Weapons</source>
-        <translation>游戏框架和武器配置</translation>
-    </message>
 </context>
 <context>
     <name>QLabel</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="353"/>
         <source>Net nick</source>
         <translation>网络游戏昵称</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Server address</source>
+        <translation type="obsolete">服务器地址</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>&lt;div align=&quot;center&quot;&gt;&lt;h1&gt;Hedgewars&lt;/h1&gt;&lt;h3&gt;Version 0.8&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;http://www.hedgewars.org/&quot;&gt;http://www.hedgewars.org/&lt;/a&gt;&lt;/p&gt;&lt;br&gt;This program is distributed under the GNU General Public License&lt;/div&gt;</source>
+        <translation type="obsolete">&lt;div align=&quot;center&quot;&gt;&lt;h1&gt;刺猬大作战&lt;/h1&gt;&lt;h3&gt;0.8&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;http://www.hedgewars.org/&quot;&gt;http://www.hedgewars.org/&lt;/a&gt;&lt;/p&gt;&lt;br&gt;This program is distributed under the GNU General Public License&lt;/div&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>&lt;h2&gt;Developers:&lt;/h2&gt;&lt;p&gt;Andrey Korotaev &amp;lt;&lt;a href=&quot;mailto:unC0Rr@gmail.com&quot;&gt;unC0Rr@gmail.com&lt;/a&gt;&amp;gt;&lt;br&gt;Igor Ulyanov &amp;lt;&lt;a href=&quot;mailto:iulyanov@gmail.com&quot;&gt;iulyanov@gmail.com&lt;/a&gt;&amp;gt;&lt;/p&gt;&lt;h2&gt;Translations:&lt;/h2&gt;english: Andrey Korotaev &amp;lt;&lt;a href=&quot;mailto:unC0Rr@gmail.com&quot;&gt;unC0Rr@gmail.com&lt;/a&gt;&amp;gt;&lt;br&gt;russian: Andrey Korotaev &amp;lt;&lt;a href=&quot;mailto:unC0Rr@gmail.com&quot;&gt;unC0Rr@gmail.com&lt;/a&gt;&amp;gt;</source>
+        <translation type="obsolete">&lt;h2&gt;Developers:&lt;/h2&gt;&lt;p&gt;Andrey Korotaev &amp;lt;&lt;a href=&quot;mailto:unC0Rr@gmail.com&quot;&gt;unC0Rr@gmail.com&lt;/a&gt;&amp;gt;&lt;br&gt;Igor Ulyanov &amp;lt;&lt;a href=&quot;mailto:iulyanov@gmail.com&quot;&gt;iulyanov@gmail.com&lt;/a&gt;&amp;gt;&lt;/p&gt;&lt;h2&gt;Translations:&lt;/h2&gt;english: Andrey Korotaev &amp;lt;&lt;a href=&quot;mailto:unC0Rr@gmail.com&quot;&gt;unC0Rr@gmail.com&lt;/a&gt;&amp;gt;&lt;br&gt;russian: Andrey Korotaev &amp;lt;&lt;a href=&quot;mailto:unC0Rr@gmail.com&quot;&gt;unC0Rr@gmail.com&lt;/a&gt;&amp;gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>difficulty:</source>
+        <translation type="obsolete">难度:</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>&lt;h3&gt;Version 0.8&lt;/h3&gt;</source>
+        <translation type="obsolete">&lt;h3&gt;版本 0.8&lt;/h3&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="46"/>
         <source>This program is distributed under the GNU General Public License</source>
-        <translation type="obsolete">This program is distributed under the GNU General Public License</translation>
+        <translation>This program is distributed under the GNU General Public License</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+        <source>&lt;h2&gt;Translations:&lt;/h2&gt;</source>
+        <translation type="obsolete">&lt;h2&gt;翻译:&lt;/h2&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+        <source>&lt;h2&gt;Developers:&lt;/h2&gt;</source>
+        <translation type="obsolete">&lt;h2&gt;开发者:&lt;/h2&gt;</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+        <source>&lt;h2&gt;Translations:&lt;/h2&gt;&lt;p&gt;</source>
+        <translation type="obsolete">&lt;h2&gt;翻译:&lt;/h2&gt;&lt;p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+        <source>&lt;h2&gt;Special thanks:&lt;/h2&gt;&lt;p&gt;</source>
+        <translation type="obsolete">&lt;h2&gt;特别感谢:&lt;/h2&gt;&lt;p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+        <source>&lt;h3&gt;Version 0.8.1&lt;/h3&gt;</source>
+        <translation type="obsolete">&lt;h3&gt;版本 0.8.1&lt;/h3&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+        <source>&lt;h2&gt;&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;</source>
+        <translation type="obsolete">&lt;h2&gt;&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="62"/>
+        <source>Turn time</source>
+        <translation type="obsolete">回合时间</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="63"/>
+        <source>Initial health</source>
+        <translation type="obsolete">初始生命值</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="687"/>
+        <source>&lt;p&gt;The best shot award was won by &lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; pts.&lt;/p&gt;</source>
+        <translation type="obsolete">&lt;p&gt;射击冠军&lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; .&lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="699"/>
+        <source>&lt;p&gt;A total of &lt;b&gt;%1&lt;/b&gt; Hedgehog(s) were killed during this round.&lt;/p&gt;</source>
+        <translation type="obsolete">&lt;p&gt;阵亡&lt;b&gt;%1&lt;/b&gt; &lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+        <source>&lt;h3&gt;Version 0.9&lt;/h3&gt;</source>
+        <translation type="obsolete">&lt;h3&gt;版本0.9&lt;/h3&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="389"/>
         <source>Resolution</source>
         <translation>分辨率</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="398"/>
         <source>FPS limit</source>
         <translation>FPS 上限</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="61"/>
         <source>Developers:</source>
         <translation>开发者:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="71"/>
         <source>Art:</source>
         <translation>艺术:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="91"/>
         <source>Translations:</source>
         <translation>翻译:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="111"/>
         <source>Special thanks:</source>
         <translation>特别感谢:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="564"/>
         <source>Server name:</source>
         <translation>服务器名:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="571"/>
         <source>Server port:</source>
         <translation>服务器端口:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/input_ip.cpp" line="32"/>
         <source>Host:</source>
         <translation>主机:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/input_ip.cpp" line="36"/>
         <source>Port:</source>
         <translation>端口:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="61"/>
         <source>Weapons</source>
         <translation>武器</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+        <source>&lt;h3&gt;Version 0.9.2&lt;/h3&gt;</source>
+        <translation type="obsolete">&lt;h3&gt;版本0.9.2&lt;/h3&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="43"/>
         <source>Version</source>
         <translation>版本</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="694"/>
+        <source>&lt;p&gt;The best shot award was won by &lt;b&gt;%1&lt;/b&gt; with &lt;b&gt;%2&lt;/b&gt; kills.&lt;/p&gt;</source>
+        <translation type="obsolete">&lt;p&gt;最佳射手&lt;b&gt;%1&lt;/b&gt;取得的战果 &lt;b&gt;%2&lt;/b&gt;&lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/about.cpp" line="83"/>
         <source>Sounds:</source>
         <translation>声音:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="424"/>
         <source>Initial sound volume</source>
         <translation>初始音量</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="923"/>
         <source>Damage Modifier</source>
-        <translation>伤害修正值</translation>
+        <translation>伤害修改</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="938"/>
         <source>Turn Time</source>
         <translation>回合时间</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="953"/>
         <source>Initial Health</source>
         <translation>初始生命值</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="968"/>
         <source>Sudden Death Timeout</source>
         <translation>死亡模式倒计时</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="980"/>
+        <source>Case Probability</source>
+        <translation type="obsolete">箱子掉落几率</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="998"/>
         <source>Scheme Name:</source>
         <translation>设置名称:</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="983"/>
         <source>Crate Drops</source>
         <translation>箱子降落</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="53"/>
         <source>Game scheme</source>
         <translation>游戏设置</translation>
     </message>
-    <message>
-        <source>Mines Time</source>
-        <translation>布雷时间</translation>
-    </message>
-    <message>
-        <source>Mines</source>
-        <translation>地雷</translation>
-    </message>
-    <message>
-        <source>% Dud Mines</source>
-        <translation>% 地雷故障</translation>
-    </message>
-    <message>
-        <source>Name</source>
-        <translation>名称</translation>
-    </message>
-    <message>
-        <source>Type</source>
-        <translation>类型</translation>
-    </message>
-    <message>
-        <source>Grave</source>
-        <translation>墓碑</translation>
-    </message>
-    <message>
-        <source>Flag</source>
-        <translation>旗帜</translation>
-    </message>
-    <message>
-        <source>Voice</source>
-        <translation>声音</translation>
-    </message>
-    <message>
-        <source>Locale</source>
-        <translation>Locale</translation>
-    </message>
-    <message>
-        <source>Restart game to apply</source>
-        <translation>需要重新启动游戏方可应用</translation>
-    </message>
-    <message>
-        <source>Explosives</source>
-        <translation>爆炸物</translation>
-    </message>
-    <message>
-        <source>Tip: </source>
-        <translation>提示:</translation>
-    </message>
-    <message>
-        <source>This development build is &apos;work in progress&apos; and may not be compatible with other versions of the game. Some features might be broken or incomplete. Use at your own risk!</source>
-        <translation>当前运行的为开发版本,不同其他版本兼容。功能或许损坏、不完整。请自行承担风险!</translation>
-    </message>
-    <message>
-        <source>Quality</source>
-        <translation>图像质量</translation>
-    </message>
-    <message>
-        <source>Sudden Death Water Rise</source>
-        <translation>死亡模式水位上涨</translation>
-    </message>
-    <message>
-        <source>Sudden Death Health Decrease</source>
-        <translation>死亡模式健康降低</translation>
-    </message>
-    <message>
-        <source>% Rope Length</source>
-        <translation>% 绳长</translation>
-    </message>
-    <message>
-        <source>% Health Crates</source>
-        <translation>% 生命箱</translation>
-    </message>
-    <message>
-        <source>Health in Crates</source>
-        <translation>生命箱的值数</translation>
-    </message>
-    <message>
-        <source>Gameplay</source>
-        <translation type="obsolete">游戏</translation>
-    </message>
-    <message>
-        <source>Stereo rendering</source>
-        <translation>立体渲染</translation>
-    </message>
-    <message>
-        <source>Style</source>
-        <translation type="unfinished">样式</translation>
-    </message>
-    <message>
-        <source>Scheme</source>
-        <translation type="unfinished">模式</translation>
-    </message>
-    <message>
-        <source>Password</source>
-        <translation>密码</translation>
-    </message>
-    <message>
-        <source>% Get Away Time</source>
-        <translation>% 脱身时间</translation>
-    </message>
-    <message>
-        <source>This program is distributed under the GNU General Public License v2</source>
-        <translation>本程序在GNU通用许可证协议第二版(GNU GPLv2)下发布</translation>
-    </message>
 </context>
 <context>
     <name>QLineEdit</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="358"/>
         <source>unnamed</source>
         <translation>无名</translation>
     </message>
-    <message>
-        <source>hedgehog %1</source>
-        <translation>刺猬 %1</translation>
-    </message>
 </context>
 <context>
     <name>QMainWindow</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>-= by unC0Rr =-</source>
+        <translation type="obsolete">-= by unC0Rr =-</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Hedgewars</source>
+        <translation type="obsolete">刺猬大作战</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/ui_hwform.cpp" line="37"/>
         <source>Hedgewars %1</source>
         <translation>刺猬大作战 %1</translation>
     </message>
@@ -1867,10 +1167,12 @@
 <context>
     <name>QMessageBox</name>
     <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="272"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="275"/>
         <source>Failed to open data directory:
 %1
 Please check your installation</source>
@@ -1879,233 +1181,292 @@
 请检查</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="685"/>
         <source>Network</source>
         <translation>网络</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/hwform.cpp" line="686"/>
         <source>Connection to server is lost</source>
-        <translation>与服务器的连接丢失</translation>
+        <translation>服务器连接丢失</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/selectWeapon.cpp" line="170"/>
         <source>Weapons</source>
         <translation>武器</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/selectWeapon.cpp" line="165"/>
         <source>Can not delete default weapon set</source>
-        <translation type="obsolete">不能删除默认武器设定</translation>
+        <translation>不能删除默认武器设定</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/selectWeapon.cpp" line="170"/>
         <source>Really delete this weapon set?</source>
         <translation>真的删除这个武器设定吗?</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/selectWeapon.cpp" line="127"/>
         <source>Can not edit default weapon set</source>
-        <translation type="obsolete">不能更改默认的武器设定</translation>
-    </message>
-    <message>
-        <source>Can not overwrite default weapon set &apos;%1&apos;!</source>
-        <translation>不能覆盖默认的武器配置 &apos;%1&apos;!</translation>
-    </message>
-    <message>
-        <source>All file associations have been set.</source>
-        <translation>所有相关文件已经设定。</translation>
-    </message>
-    <message>
-        <source>Teams</source>
-        <translation>队伍</translation>
-    </message>
-    <message>
-        <source>Really delete this team?</source>
-        <translation>真的要删除队伍?</translation>
-    </message>
-    <message>
-        <source>Schemes</source>
-        <translation>游戏框架</translation>
-    </message>
-    <message>
-        <source>Can not delete default scheme &apos;%1&apos;!</source>
-        <translation>无法删除默认游戏框架 &apos;%1&apos;!</translation>
-    </message>
-    <message>
-        <source>File association failed.</source>
-        <translation>文件关联失败。</translation>
-    </message>
-    <message>
-        <source>Really delete this game scheme?</source>
-        <translation>真的删除此游戏框架?</translation>
-    </message>
-    <message>
-        <source>Can not delete default weapon set &apos;%1&apos;!</source>
-        <translation>无法删除武器配置%1&apos;!</translation>
+        <translation>不能更改默认的武器设定</translation>
     </message>
 </context>
 <context>
     <name>QObject</name>
     <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="35"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="36"/>
         <source>Cannot create directory %1</source>
         <translation>无法创建路径 %1</translation>
     </message>
     <message>
-        <source>OK</source>
-        <translation>确认</translation>
+        <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+        <source>Quit</source>
+        <translation type="obsolete">退出</translation>
     </message>
     <message>
-        <source>Nickname</source>
-        <translation>昵称</translation>
-    </message>
-    <message>
-        <source>Please enter your nickname</source>
-        <translation>请输入您的昵称</translation>
+        <location filename="../../../../QTfrontend/main.cpp" line="37"/>
+        <source>OK</source>
+        <translation>确认</translation>
     </message>
 </context>
 <context>
     <name>QPushButton</name>
     <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+        <source>Single Player</source>
+        <translation type="obsolete">单人游戏</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+        <source>Multiplayer</source>
+        <translation type="obsolete">多人游戏</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+        <source>Net game</source>
+        <translation type="obsolete">网络游戏</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+        <source>Demos</source>
+        <translation type="obsolete">Demo</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="237"/>
         <source>Setup</source>
         <translation>设置</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+        <source>Exit</source>
+        <translation type="obsolete">退出</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+        <source>Back</source>
+        <translation type="obsolete">返回</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+        <source>Simple Game</source>
+        <translation type="obsolete">简单游戏</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+        <source>Discard</source>
+        <translation type="obsolete">中止</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+        <source>Save</source>
+        <translation type="obsolete">保存</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="75"/>
         <source>Play demo</source>
         <translation>播放 demo</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+        <source>New team</source>
+        <translation type="obsolete">新队伍</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+        <source>Edit team</source>
+        <translation type="obsolete">编辑队伍</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="482"/>
         <source>Connect</source>
         <translation>连接</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Disconnect</source>
+        <translation type="obsolete">失去连接</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Join</source>
+        <translation type="obsolete">加入</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Create</source>
+        <translation type="obsolete">创建</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Add Team</source>
+        <translation type="obsolete">添加队伍</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="714"/>
         <source>Go!</source>
         <translation>上场!</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="544"/>
         <source>Start</source>
         <translation>开始</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>About</source>
+        <translation type="obsolete">关于</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="466"/>
         <source>Start server</source>
         <translation>开始服务端</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="491"/>
         <source>Update</source>
         <translation>更新</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+        <source>Waiting</source>
+        <translation type="obsolete">等待中</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="80"/>
         <source>Load</source>
         <translation>读取</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+        <source>Weapons scheme</source>
+        <translation type="obsolete">武器设定</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+        <source>Training</source>
+        <translation type="obsolete">训练</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="496"/>
         <source>Specify</source>
         <translation>指定</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="580"/>
         <source>default</source>
         <translation>默认</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="48"/>
         <source>Rename</source>
         <translation>重命名</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/input_ip.cpp" line="52"/>
         <source>OK</source>
         <translation>确定</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/input_ip.cpp" line="57"/>
         <source>Cancel</source>
         <translation>取消</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/playrecordpage.cpp" line="52"/>
         <source>Delete</source>
         <translation>删除</translation>
     </message>
     <message>
-        <source>Ready</source>
-        <translation>准备好了</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="501"/>
+        <source>Join official server</source>
+        <translation type="obsolete">加入官方服务器</translation>
     </message>
     <message>
-        <source>Random Team</source>
-        <translation>随机分配队伍</translation>
-    </message>
-    <message>
-        <source>Associate file extensions</source>
-        <translation>相关文件扩展</translation>
-    </message>
-    <message>
-        <source>more</source>
-        <translation>更多</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="618"/>
+        <source>Ready</source>
+        <translation>准备好了</translation>
     </message>
 </context>
 <context>
     <name>QTableWidget</name>
     <message>
-        <source>Room Name</source>
-        <translation>房间名</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="787"/>
+        <source>Room name</source>
+        <translation>房间名称</translation>
     </message>
     <message>
-        <source>C</source>
-        <translation>人数限制</translation>
-    </message>
-    <message>
-        <source>T</source>
-        <translation>时间限制</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="788"/>
+        <source>Players number</source>
+        <translation>玩家数量</translation>
     </message>
     <message>
-        <source>Owner</source>
-        <translation>创建者</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="790"/>
+        <source>Round in progress</source>
+        <translation>回合数</translation>
+    </message>
+</context>
+<context>
+    <name>QToolBox</name>
+    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="242"/>
+        <source>Actions</source>
+        <translation>行动</translation>
     </message>
     <message>
-        <source>Map</source>
-        <translation>地图</translation>
-    </message>
-    <message>
-        <source>Rules</source>
-        <translation>规则</translation>
-    </message>
-    <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="244"/>
         <source>Weapons</source>
         <translation>武器</translation>
     </message>
-</context>
-<context>
-    <name>SelWeaponWidget</name>
     <message>
-        <source>Weapon set</source>
-        <translation>武器设置</translation>
-    </message>
-    <message>
-        <source>Probabilities</source>
-        <translation>空中支援几率</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="246"/>
+        <source>Weapon properties</source>
+        <translation>武器选项</translation>
     </message>
     <message>
-        <source>Ammo in boxes</source>
-        <translation>弹药箱</translation>
-    </message>
-    <message>
-        <source>Delays</source>
-        <translation>延迟回合数</translation>
-    </message>
-    <message>
-        <source>new</source>
-        <translation>新</translation>
-    </message>
-    <message>
-        <source>copy of</source>
-        <translation>副本</translation>
+        <location filename="../../../../QTfrontend/pages.cpp" line="248"/>
+        <source>Other</source>
+        <translation>其他</translation>
     </message>
 </context>
 <context>
     <name>TCPBase</name>
     <message>
+        <location filename="../../../../QTfrontend/tcpBase.cpp" line="99"/>
         <source>Error</source>
         <translation>错误</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/tcpBase.cpp" line="45"/>
         <source>Unable to start the server: %1.</source>
         <translation>无法开始服务端: %1.</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/tcpBase.cpp" line="101"/>
         <source>Unable to run engine: %1 (</source>
         <translation>无法运行引擎: %1 (</translation>
     </message>
@@ -2113,678 +1474,362 @@
 <context>
     <name>ToggleButtonWidget</name>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="888"/>
         <source>Fort Mode</source>
         <translation>城堡模式</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="892"/>
         <source>Divide Teams</source>
         <translation>团体行动</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="896"/>
         <source>Solid Land</source>
         <translation>固实地面</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="900"/>
         <source>Add Border</source>
         <translation>添加边界</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="904"/>
         <source>Low Gravity</source>
         <translation>低重力</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="908"/>
         <source>Laser Sight</source>
         <translation>激光瞄准</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="912"/>
         <source>Invulnerable</source>
         <translation>刀枪不入</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/pages.cpp" line="916"/>
         <source>Add Mines</source>
-        <translation type="obsolete">布置地雷</translation>
-    </message>
-    <message>
-        <source>Vampirism</source>
-        <translation>吸血鬼</translation>
-    </message>
-    <message>
-        <source>Karma</source>
-        <translation>因果报应</translation>
-    </message>
-    <message>
-        <source>Artillery</source>
-        <translation>射术</translation>
-    </message>
-    <message>
-        <source>Random Order</source>
-        <translation>随机顺序</translation>
-    </message>
-    <message>
-        <source>King</source>
-        <translation>国王模式</translation>
-    </message>
-    <message>
-        <source>Place Hedgehogs</source>
-        <translation>手动放置刺猬</translation>
-    </message>
-    <message>
-        <source>Clan Shares Ammo</source>
-        <translation>团队共享弹药</translation>
-    </message>
-    <message>
-        <source>Disable Girders</source>
-        <translation>禁止梁</translation>
-    </message>
-    <message>
-        <source>Disable Land Objects</source>
-        <translation>禁止地面物件</translation>
-    </message>
-    <message>
-        <source>AI Survival Mode</source>
-        <translation>AI生存模式</translation>
-    </message>
-    <message>
-        <source>Unlimited Attacks</source>
-        <translation>无限攻击</translation>
-    </message>
-    <message>
-        <source>Reset Weapons</source>
-        <translation>重置武器</translation>
-    </message>
-    <message>
-        <source>Per Hedgehog Ammo</source>
-        <translation>每个刺猬的弹药</translation>
-    </message>
-    <message>
-        <source>Reset Health</source>
-        <translation>重置生命值</translation>
-    </message>
-    <message>
-        <source>Disable Wind</source>
-        <translation>禁止风力作用</translation>
-    </message>
-    <message>
-        <source>More Wind</source>
-        <translation>让风来地更猛烈吧</translation>
-    </message>
-    <message>
-        <source>Tag Team</source>
-        <translation>为队伍添加标签</translation>
-    </message>
-    <message>
-        <source>Add Bottom Border</source>
-        <translation>添加底部边界</translation>
+        <translation>布置地雷</translation>
     </message>
 </context>
 <context>
     <name>binds</name>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="23"/>
         <source>up</source>
         <translation>上</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="24"/>
         <source>left</source>
         <translation>左</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="25"/>
         <source>right</source>
         <translation>右</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="26"/>
         <source>down</source>
         <translation>下</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="28"/>
+        <source>jump</source>
+        <translation>跳</translation>
+    </message>
+    <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="29"/>
         <source>attack</source>
         <translation>攻击</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="31"/>
         <source>put</source>
         <translation>放</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="32"/>
         <source>switch</source>
         <translation>切换</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="35"/>
         <source>slot 1</source>
         <translation>slot 1</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="36"/>
         <source>slot 2</source>
         <translation>slot 2</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="37"/>
         <source>slot 3</source>
         <translation>slot 3</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="38"/>
         <source>slot 4</source>
         <translation>slot 4</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="39"/>
         <source>slot 5</source>
         <translation>slot 5</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="40"/>
         <source>slot 6</source>
         <translation>slot 6</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="41"/>
         <source>slot 7</source>
         <translation>slot 7</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="42"/>
         <source>slot 8</source>
         <translation>slot 8</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="44"/>
         <source>timer 1 sec</source>
         <translation>定时1秒</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="45"/>
         <source>timer 2 sec</source>
         <translation>定时2秒</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="46"/>
         <source>timer 3 sec</source>
         <translation>定时3秒</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="47"/>
         <source>timer 4 sec</source>
         <translation>定时4秒</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="48"/>
         <source>timer 5 sec</source>
         <translation>定时5秒</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="56"/>
         <source>capture</source>
-        <translation>截取</translation>
+        <translation>夺取</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="58"/>
         <source>quit</source>
         <translation>退出</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="33"/>
         <source>find hedgehog</source>
-        <translation>寻找刺猬</translation>
+        <translation>找到 刺猬</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="34"/>
         <source>ammo menu</source>
         <translation>弹药菜单</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="53"/>
         <source>volume down</source>
         <translation>降低音量</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="54"/>
         <source>volume up</source>
         <translation>提高音量</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="55"/>
         <source>change mode</source>
         <translation>改变模式</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="51"/>
         <source>pause</source>
         <translation>暂停</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="43"/>
         <source>slot 9</source>
         <translation>slot 9</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="57"/>
         <source>hedgehogs
 info</source>
         <translation>刺猬大作战
 信息</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="49"/>
         <source>chat</source>
         <translation>聊天</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="50"/>
         <source>chat history</source>
         <translation>聊天记录</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="52"/>
         <source>confirmation</source>
         <translation>确认</translation>
     </message>
     <message>
+        <location filename="../../../../QTfrontend/binds.cpp" line="30"/>
         <source>precise aim</source>
         <translation>练习瞄准</translation>
     </message>
-    <message>
-        <source>zoom in</source>
-        <translation>放大</translation>
-    </message>
-    <message>
-        <source>zoom out</source>
-        <translation>缩小</translation>
-    </message>
-    <message>
-        <source>reset zoom</source>
-        <translation>重置</translation>
-    </message>
-    <message>
-        <source>long jump</source>
-        <translation>远跳</translation>
-    </message>
-    <message>
-        <source>high jump</source>
-        <translation>高跳</translation>
-    </message>
-    <message>
-        <source>slot 10</source>
-        <translation>slot 10</translation>
-    </message>
-</context>
-<context>
-    <name>binds (categories)</name>
-    <message>
-        <source>Basic controls</source>
-        <translation>基本控制</translation>
-    </message>
-    <message>
-        <source>Weapon controls</source>
-        <translation>武器控制</translation>
-    </message>
-    <message>
-        <source>Camera and cursor controls</source>
-        <translation>镜头和光标控制</translation>
-    </message>
-    <message>
-        <source>Other</source>
-        <translation>其他</translation>
-    </message>
-</context>
-<context>
-    <name>binds (descriptions)</name>
-    <message>
-        <source>Move your hogs and aim:</source>
-        <translation>移动您的刺猬同时瞄准:</translation>
-    </message>
-    <message>
-        <source>Traverse gaps and obstacles by jumping:</source>
-        <translation>使用跳跃越过沟渠、障碍:</translation>
-    </message>
-    <message>
-        <source>Fire your selected weapon or trigger an utility item:</source>
-        <translation>使用选择的武器开火、使用物品:</translation>
-    </message>
-    <message>
-        <source>Pick a weapon or a target location under the cursor:</source>
-        <translation>选取一个武器或者瞄准光标下的地点:</translation>
-    </message>
-    <message>
-        <source>Switch your currently active hog (if possible):</source>
-        <translation>切换到您当前活动的刺猬(如果可用):</translation>
-    </message>
-    <message>
-        <source>Pick a weapon or utility item:</source>
-        <translation>选择一个武器或物品:</translation>
-    </message>
-    <message>
-        <source>Set the timer on bombs and timed weapons:</source>
-        <translation>设置定时炸弹等武器起爆时间:</translation>
-    </message>
-    <message>
-        <source>Move the camera to the active hog:</source>
-        <translation>移动镜头到选中的刺猬:</translation>
-    </message>
-    <message>
-        <source>Move the cursor or camera without using the mouse:</source>
-        <translation>不用鼠标移动光标或镜头:</translation>
-    </message>
-    <message>
-        <source>Modify the camera&apos;s zoom level:</source>
-        <translation>调整镜头放大倍数:</translation>
-    </message>
-    <message>
-        <source>Talk to your team or all participants:</source>
-        <translation>同队友或全部参与者对话:</translation>
-    </message>
-    <message>
-        <source>Pause, continue or leave your game:</source>
-        <translation>暂停、继续或离开游戏:</translation>
-    </message>
-    <message>
-        <source>Modify the game&apos;s volume while playing:</source>
-        <translation>调整游戏时音量:</translation>
-    </message>
-    <message>
-        <source>Toggle fullscreen mode:</source>
-        <translation>全屏模式:</translation>
-    </message>
-    <message>
-        <source>Take a screenshot:</source>
-        <translation>截图:</translation>
-    </message>
-    <message>
-        <source>Toggle labels above hedgehogs:</source>
-        <translation>切换刺猬头顶标签的显示方式:</translation>
-    </message>
 </context>
 <context>
-    <name>binds (keys)</name>
-    <message>
-        <source>Axis</source>
-        <translation>Axis轴</translation>
-    </message>
+    <name>teams</name>
     <message>
-        <source>(Up)</source>
-        <translation>(上)</translation>
-    </message>
-    <message>
-        <source>(Down)</source>
-        <translation>(下)</translation>
-    </message>
-    <message>
-        <source>Hat</source>
-        <translation>帽子</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="53"/>
+        <source>Hedgehogs</source>
+        <translation>刺猬 </translation>
     </message>
     <message>
-        <source>(Left)</source>
-        <translation>(左)</translation>
-    </message>
-    <message>
-        <source>(Right)</source>
-        <translation>(右)</translation>
-    </message>
-    <message>
-        <source>Button</source>
-        <translation>按键</translation>
-    </message>
-    <message>
-        <source>Keyboard</source>
-        <translation>键盘</translation>
-    </message>
-    <message>
-        <source>Mouse: Left button</source>
-        <translation>鼠标:左键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="54"/>
+        <source>hedgehog 1</source>
+        <translation>刺猬 1号</translation>
     </message>
     <message>
-        <source>Mouse: Middle button</source>
-        <translation>鼠标:中键</translation>
-    </message>
-    <message>
-        <source>Mouse: Right button</source>
-        <translation>鼠标:右键</translation>
-    </message>
-    <message>
-        <source>Mouse: Wheel up</source>
-        <translation>鼠标滚轮:向上</translation>
-    </message>
-    <message>
-        <source>Mouse: Wheel down</source>
-        <translation>鼠标滚轮:向下</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="55"/>
+        <source>hedgehog 2</source>
+        <translation>刺猬 2号</translation>
     </message>
     <message>
-        <source>Backspace</source>
-        <translation>退格键</translation>
-    </message>
-    <message>
-        <source>Tab</source>
-        <translation>Tab</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="56"/>
+        <source>hedgehog 3</source>
+        <translation>刺猬 3号</translation>
     </message>
     <message>
-        <source>Clear</source>
-        <translation>Num Lock / Clear</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="57"/>
+        <source>hedgehog 4</source>
+        <translation>刺猬 4号</translation>
     </message>
     <message>
-        <source>Return</source>
-        <translation>回车</translation>
-    </message>
-    <message>
-        <source>Pause</source>
-        <translation>暂停键</translation>
-    </message>
-    <message>
-        <source>Escape</source>
-        <translation>ESC键(退出键)</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="58"/>
+        <source>hedgehog 5</source>
+        <translation>刺猬 5号</translation>
     </message>
     <message>
-        <source>Space</source>
-        <translation>空格键</translation>
-    </message>
-    <message>
-        <source>Delete</source>
-        <translation>Del(删除键)</translation>
-    </message>
-    <message>
-        <source>Numpad 0</source>
-        <translation>小键盘0</translation>
-    </message>
-    <message>
-        <source>Numpad 1</source>
-        <translation>小键盘1</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="59"/>
+        <source>hedgehog 6</source>
+        <translation>刺猬 6号</translation>
     </message>
     <message>
-        <source>Numpad 2</source>
-        <translation>小键盘2</translation>
-    </message>
-    <message>
-        <source>Numpad 3</source>
-        <translation>小键盘3</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="60"/>
+        <source>hedgehog 7</source>
+        <translation>刺猬 7号</translation>
     </message>
     <message>
-        <source>Numpad 4</source>
-        <translation>小键盘4</translation>
-    </message>
-    <message>
-        <source>Numpad 5</source>
-        <translation>小键盘5</translation>
-    </message>
-    <message>
-        <source>Numpad 6</source>
-        <translation>小键盘6</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="61"/>
+        <source>hedgehog 8</source>
+        <translation>刺猬 8号</translation>
     </message>
     <message>
-        <source>Numpad 7</source>
-        <translation>小键盘7</translation>
-    </message>
-    <message>
-        <source>Numpad 8</source>
-        <translation>小键盘8</translation>
-    </message>
-    <message>
-        <source>Numpad 9</source>
-        <translation>小键盘9</translation>
-    </message>
-    <message>
-        <source>Numpad .</source>
-        <translation>小键盘.</translation>
-    </message>
-    <message>
-        <source>Numpad /</source>
-        <translation>小键盘/</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="73"/>
+        <source>Goddess</source>
+        <translation>女神</translation>
     </message>
     <message>
-        <source>Numpad *</source>
-        <translation>小键盘*</translation>
-    </message>
-    <message>
-        <source>Numpad -</source>
-        <translation>小键盘-</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="74"/>
+        <source>Isis</source>
+        <translation>艾希丝</translation>
     </message>
     <message>
-        <source>Numpad +</source>
-        <translation>小键盘+</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="75"/>
+        <source>Astarte</source>
+        <translation>阿斯德尔特</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <translation>回车键</translation>
-    </message>
-    <message>
-        <source>Equals</source>
-        <translation>等于</translation>
-    </message>
-    <message>
-        <source>Up</source>
-        <translation>上</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="76"/>
+        <source>Diana</source>
+        <translation>黛安娜</translation>
     </message>
     <message>
-        <source>Down</source>
-        <translation>下</translation>
-    </message>
-    <message>
-        <source>Right</source>
-        <translation>右</translation>
-    </message>
-    <message>
-        <source>Left</source>
-        <translation>左</translation>
-    </message>
-    <message>
-        <source>Insert</source>
-        <translation>插入键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="77"/>
+        <source>Aphrodite</source>
+        <translation>阿弗罗狄特</translation>
     </message>
     <message>
-        <source>Home</source>
-        <translation>Home键</translation>
-    </message>
-    <message>
-        <source>End</source>
-        <translation>End键</translation>
-    </message>
-    <message>
-        <source>Page up</source>
-        <translation>向上翻页键</translation>
-    </message>
-    <message>
-        <source>Page down</source>
-        <translation>向下翻页键</translation>
-    </message>
-    <message>
-        <source>Num lock</source>
-        <translation>小键盘数字锁</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="78"/>
+        <source>Hecate</source>
+        <translation>赫卡特</translation>
     </message>
     <message>
-        <source>Caps lock</source>
-        <translation>大小写切换键</translation>
-    </message>
-    <message>
-        <source>Scroll lock</source>
-        <translation>Scroll Lock键</translation>
-    </message>
-    <message>
-        <source>Right shift</source>
-        <translation>右Shift键</translation>
-    </message>
-    <message>
-        <source>Left shift</source>
-        <translation>左Shift键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="79"/>
+        <source>Demeter</source>
+        <translation>得墨忒耳</translation>
     </message>
     <message>
-        <source>Right ctrl</source>
-        <translation>右Ctrl键</translation>
-    </message>
-    <message>
-        <source>Left ctrl</source>
-        <translation>左Ctrl键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="80"/>
+        <source>Kali</source>
+        <translation>迦梨</translation>
     </message>
     <message>
-        <source>Right alt</source>
-        <translation>右Alt键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="81"/>
+        <source>Inanna</source>
+        <translation>維納斯</translation>
     </message>
     <message>
-        <source>Left alt</source>
-        <translation>左Alt键</translation>
-    </message>
-    <message>
-        <source>Right meta</source>
-        <translation>右meta键</translation>
-    </message>
-    <message>
-        <source>Left meta</source>
-        <translation>左meta键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="93"/>
+        <source>Fruits</source>
+        <translation>水果</translation>
     </message>
     <message>
-        <source>A button</source>
-        <translation>A 键</translation>
-    </message>
-    <message>
-        <source>B button</source>
-        <translation>B 键</translation>
-    </message>
-    <message>
-        <source>X button</source>
-        <translation>X 键</translation>
-    </message>
-    <message>
-        <source>Y button</source>
-        <translation>Y 键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="94"/>
+        <source>Banana</source>
+        <translation>香蕉</translation>
     </message>
     <message>
-        <source>LB button</source>
-        <translation>LB 键</translation>
-    </message>
-    <message>
-        <source>RB button</source>
-        <translation>RB 键</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="95"/>
+        <source>Apple</source>
+        <translation>苹果</translation>
     </message>
     <message>
-        <source>Back button</source>
-        <translation>返回键</translation>
-    </message>
-    <message>
-        <source>Start button</source>
-        <translation>开始键</translation>
-    </message>
-    <message>
-        <source>Left stick</source>
-        <translation>左摇杆</translation>
-    </message>
-    <message>
-        <source>Right stick</source>
-        <translation>右摇杆</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="96"/>
+        <source>Orange</source>
+        <translation>橙子</translation>
     </message>
     <message>
-        <source>Left stick (Right)</source>
-        <translation>右(左摇杆)</translation>
-    </message>
-    <message>
-        <source>Left stick (Left)</source>
-        <translation>左(左摇杆)</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="97"/>
+        <source>Lemon</source>
+        <translation>柠檬</translation>
     </message>
     <message>
-        <source>Left stick (Down)</source>
-        <translation>下(左摇杆)</translation>
-    </message>
-    <message>
-        <source>Left stick (Up)</source>
-        <translation>上(左摇杆)</translation>
-    </message>
-    <message>
-        <source>Left trigger</source>
-        <translation>左制动</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="98"/>
+        <source>Pineapple</source>
+        <translation>菠萝</translation>
     </message>
     <message>
-        <source>Right trigger</source>
-        <translation>右制动</translation>
-    </message>
-    <message>
-        <source>Right stick (Down)</source>
-        <translation>下(右摇杆)</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="99"/>
+        <source>Mango</source>
+        <translation>芒果</translation>
     </message>
     <message>
-        <source>Right stick (Up)</source>
-        <translation>上(右摇杆)</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="100"/>
+        <source>Peach</source>
+        <translation>桃子</translation>
     </message>
     <message>
-        <source>Right stick (Right)</source>
-        <translation>右(右摇杆)</translation>
-    </message>
-    <message>
-        <source>Right stick (Left)</source>
-        <translation>左(右摇杆)</translation>
-    </message>
-    <message>
-        <source>DPad</source>
-        <translation>DPad板</translation>
+        <location filename="../../../../QTfrontend/predefteams.h" line="101"/>
+        <source>Plum</source>
+        <translation>梅子</translation>
     </message>
 </context>
 </TS>
--- a/share/hedgewars/Data/Locale/zh_CN.txt	Thu Aug 30 12:47:41 2012 -0400
+++ b/share/hedgewars/Data/Locale/zh_CN.txt	Thu Aug 30 13:02:19 2012 -0400
@@ -1,825 +1,47 @@
 ; Simplified Chinese locale
 
 00:00=手榴弹
-00:01=集束炸弹
-00:02=火箭筒
-00:03=归巢的蜜蜂
-00:04=霰弹枪
-00:05=大锤
-00:06=跳过回合
+00:01=子母炸弹
+00:02=火箭炮
+00:03=UFO
+00:04=散弹枪
+00:05=气锤
+00:06=掠过
 00:07=绳索
 00:08=地雷
 00:09=沙漠之鹰
 00:10=炸药
 00:11=球棒
-00:12=Shoryuken
+00:12=升龙拳
 00:13=秒
 00:14=降落伞
 00:15=空袭
-00:16=地雷空袭
+00:16=地雷袭击
 00:17=喷灯
-00:18=钢梁
+00:18=钢板
 00:19=传送
-00:20=切换刺猬
+00:20=切换
 00:21=迫击炮
 00:22=鞭子
 00:23=神风特工队
 00:24=蛋糕
-00:25=引诱
+00:25=吸引
 00:26=西瓜炸弹
 00:27=地狱礼花
-00:28=钻头火箭
+00:28=钻地火箭
 00:29=弹珠炮
-00:30=汽油弹空袭
-00:31=遥控轰炸机
+00:30=燃烧弹
+00:31=轰炸机
 00:32=低重力
-00:33=增强伤害
-00:34=无敌
+00:33=附加伤害
+00:34=刀枪不入
 00:35=加时
 00:36=激光瞄准
-00:37=吸血
-00:38=狙击枪
-00:39=UFO
-00:40=燃烧瓶
-00:41=鸟儿
-00:42=传送器
-00:43=飞来的钢琴
-00:44=毒奶酪
-00:45=正弦能量炮
-00:46=火焰喷射器
-00:47=固定地雷
-00:48=大锤
-00:49=复苏
-00:50=电钻空袭
-00:51=土块
 
-01:00=开战!
-01:01=平局
-01:02= %1 胜利!
+01:00=战斗啦!
+01:01=平手
+01:02= %1 胜!
 01:03=音量 %1%
 01:04=暂停
-01:05=确定要退出? (是Y/否Esc)
-01:06=死亡模式!
-01:07=%1 剩余
-01:08=燃料
-01:09=同步中...
-01:10=使用本工具不会结束回合!
-01:11=您还不能用它!
-01:12=死亡模式前最后一回合!
-01:13=%1 回合倒计时!
-01:14=预备上, %1!
-
-; Event messages
-; Hog (%1) died
-; 02:00=%1 has kicked the bucket!
-02:00=%1 翘辫子了!
-; 02:00=%1 has seen the light!
-02:00=%1 目睹圣光降临!
-; 02:00=%1 never saw that coming!
-02:00=%1 无法瞑目!
-; 02:00=%1 waves goodbye!
-02:00=%1 向大家挥手道别。
-; 02:00=%1 has gone to a better place!
-02:00=%1 去了极乐世界!
-; 02:00=%1 meets his maker!
-02:00=%1 去见造物主了!
-; 02:00=%1 can hang on no longer!
-02:00=%1 再也受不了了!
-; 02:00=%1 has done his duty!
-02:00=%1 完成了他的使命!
-; 02:00=%1 makes the ultimate sacrifice!
-02:00=%1 做了最大的牺牲!
-; 02:00=%1 departs this mortal coil!
-02:00=%1 摆脱了躯壳的束缚!
-; 02:00=%1 makes like a tree and leaves!
-02:00=%1 叶落归根。
-; 02:00=%1 has timed out!
-02:00=%1 大限已至。
-; 02:00=%1 says peace out!
-02:00=%1 悄然离场了。
-; 02:00=%1 will be fondly remembered!
-02:00=%1 永远活在我们心中!
-; 02:00=%1 has an aneurysm!
-02:00=%1 不治而亡。
-; 02:00=%1 leaves behind a wife and child
-02:00=%1 留下一家孤儿寡母
-; 02:00=%1 has launched his last bazooka
-02:00=%1 发射了最后一发火箭弹
-; 02:00=%1 has tossed his last grenade
-02:00=%1 扔出了最后一枚手榴弹
-; 02:00=%1 has baked his last cake
-02:00=%1 烘烤了最后一块蛋糕
-; 02:00=%1 has swung on his last rope
-02:00=%1 最后一次甩出了绳索
-; 02:00=%1 has called his last airstrike
-02:00=%1 最后一次呼叫空袭
-; 02:00=%1 has pumped his last shotgun
-02:00=%1 最后一次抽出了霰弹枪
-; 02:00=%1 has thrown his last melon
-02:00=%1 最后一次扔出了西瓜炸弹
-; 02:00=%1 has drawn his last deagle
-02:00=%1 最后一次拔出了沙漠之鹰
-; 02:00=%1 took one shot too many
-02:00=%1 挨了太多枪子了
-; 02:00=%1 could really have used a health crate
-02:00=%1 真该用下医疗包的
-; 02:00=%1 has gone to play a better game
-02:00=%1 去玩更有意思的游戏去了
-; 02:00=%1 has ragequit life
-02:00=%1 拔网线了!
-; 02:00=%1 fails
-02:00=%1 失败了
-; 02:00=Poor poor %1...
-02:00=可怜的 %1...
-; 02:00=%1 prefers wormux
-02:00=%1 更喜欢 Warmux
-; 02:00=%1 has been blocking shots with his face
-02:00=%1 勇于面对子弹,结果相当惨烈
-; 02:00=%1 is a hero amongst me...err..hogs
-02:00=%1 是位英雄……额……在刺猬当中
-; 02:00=%1 finds his place in Valhalla
-02:00=%1 在勇者纪念碑上找到了自己的位置
-; 02:00=%1 has left the building
-02:00=%1 离开了这间屋子
-; 02:00=%1 goes the way of the dinosaurs
-02:00=%1 步上了恐龙的道路
-; 02:00=%1 brings hedgehogs one step closer to extinction
-02:00=%1 让刺猬向物种灭绝更近了一步
-; 02:00=%1 brings a tear to my eye
-02:00=%1 带走了我一滴眼泪
-; 02:00=%1 is an ex-hog
-02:00=%1 生前是一只刺猬
-; 02:00=%1 is pushing up the daisies
-02:00=%1 被菊花簇拥
-; 02:00=%1 has ceased to be
-02:00=%1 被删除了
-; 02:00=Say goodbye to %1
-02:00=对 %1 说再见
-; 02:00=No hope left for %1
-02:00=%1 没有希望了
-; 02:00=%1 faces the final curtain
-02:00=%1 面容被落下的帷幕遮住了
-; 02:00=Smoke 'em if you got 'em, %1
-02:00=%1 抓紧时间实现你最后的愿望吧
-; 02:00=%1 suffers a Spontaneous Massive Existence Failure
-02:00=%1 遭遇了自发性大规模故障
-; 02:00=%1 has passed on
-02:00=%1 走了
-; 02:00=%1 is stone dead
-02:00=%1 永垂不朽
-; 02:00=%1 is no more
-02:00=%1 不在了
-; 02:00=%1 has expired
-02:00=%1 已故
-; 02:00=Bereft of life, %1 rests in peace
-02:00=%1 安详地躺着
-; 02:00=%1 joins the choir invisible
-02:00=%1 加入了隐形唱诗班
-; 02:00=Farewell %1, we hardly knew ye!
-02:00=%1, 永别了,我们还没熟悉你呢!
-; 02:00=%1 had a low tolerance for being shot
-02:00=%1 抗打击能力不足
-; 02:00=%1 could have used an extra life
-02:00=%1 本该有第二条命的
-; 02:00=Is there a doctor in the house?
-02:00=这里有医生吗?
-
-; Hog (%1) drowned
-; 02:01=%1 plays submarine!
-02:01=%1 去玩潜水艇了!
-; 02:01=%1 mimics the Titanic!
-02:01=%1 学泰坦尼克去了!
-; 02:01=%1 swims like a stone!
-02:01=%1 石沉大海!
-;02:01=%1 checks out the deep end
-02:01=%1 说要去检查深水区
-;02:01=%1 goes glug glug glug
-02:01=%1 :“咕噜咕噜咕噜……”
-;02:01=%1 goes splash
-02:01=%1 栽入水花里
-;02:01=%1 forgot his armbands
-02:01=%1 忘记了戴臂章
-;02:01=%1 really should have taken swimming lessons
-02:01=%1 真的该去学游泳的
-;02:01=%1 left his surfboard at home
-02:01=%1 把救生圈忘家了
-;02:01=%1 is washed up
-02:01=%1 被冲走了
-;02:01=%1 is one soggy hog
-02:01=%1 脑子进水了
-;02:01=%1 forgot to bring his life jacket
-02:01=%1 忘记带救生衣了
-;02:01=%1 goes splish splash splish
-02:01=%1 实现了水上飘,身后一片水花荡漾
-;02:01=%1 is sleeping with the fishes
-02:01=%1 将会和鱼睡在一起
-;02:01=%1 thinks the water physics suck in this game
-02:01=%1 认为这游戏关于落水的设定糟糕透了
-;02:01=%1 looks thirsty
-02:01=%1 看样子很渴
-;02:01=the sea claims %1
-02:01=大海吞没了 %1
-;02:01=%1 is lost at sea
-02:01=%1 在海上迷失了
-;02:01=%1 should have brought his scuba gear
-02:01=%1 应该要带潜水工具的
-;02:01=%1 gets a burial at sea
-02:01=%1 享受到了海葬待遇
-;02:01=%1 has that sinking feeling
-02:01=%1 觉得自己在下沉
-;02:01=%1 is practicing his backstroke
-02:01=%1 终于能实践自己的游泳理论了
-;02:01=%1 goes in search of the Titanic
-02:01=%1 去泰坦尼克号寻宝了
-;02:01=%1 is not Jesus
-02:01=很遗憾 %1 不是耶稣
-;02:01=%1 is finding Nemo
-02:01=%1 正在寻找Nemo
-;02:01=%1 springs a leak
-02:01=%1 钻入了一个水洼
-;02:01=You've gotta wonder how many hogs are down there
-02:01=你会知道海底还会有多少同伴的
-;02:01=%1 makes the ocean slightly higher
-02:01=%1 让海平面高了那么一点, 就一点
-;02:01=%1 didn't enlist in the Navy
-02:01=很明显 %1 没在海军服役过
-;02:01=%1 is doing his impersonation of a dead fish
-02:01=%1 其实是在模仿死鱼啦
-;02:01=At least you didn't go down the toilet, %1
-02:01=还好 %1 你不是掉进了厕所
-;02:01=Sonic couldn't swim and neither can %1
-02:01=索尼克不能游泳, %1 也一样
-;02:01=%1 wants to play Ecco the dolphin
-02:01=%1 想玩海底漫步
-;02:01=%1 has gone to visit Aquaria
-02:01=%1 去水族馆报到了
-;02:01=%1 has found the lost city of Atlantis
-02:01=%1 找到了传说中的亚特兰蒂斯城
-;02:01=%1 aims for the lead role in Bioshock 3
-02:01=%1 立志在生化奇兵3中担任头号角色
-;02:01=Your doggy paddle could use a little work, %1
-02:01=你的狗刨式会有点用的, %1
-;02:01=%1 should have brought a jet ski
-02:01=%1 竟然没带摩托艇
-;02:01=%1 doesn't like watersports
-02:01=%1 不喜欢水上运动
-;02:01=%1 is forever blowing bubbles
-02:01=%1 学会了绝技: 神风吹泡泡
-;02:01=%1 is short of a raft
-02:01=%1 需要一个救生艇
-;02:01=%1 thinks salt water is good for the skin
-02:01=%1 认为盐水对皮肤有好处
-;02:01=%1 gets salt water in his wounds
-02:01=%1 的伤口沾上了盐水
-;02:01=%1 has walked the plank
-02:01=%1 错过了那块木板
-;02:01=%1 has a bath
-02:01=%1 洗澡去了
-;02:01=%1 is wet wet wet
-02:01=%1 全身都是水,水,水……
-;02:01=%1 gets his quills wet
-02:01=%1 把刺全弄湿了
-;02:01=It's Davy Jones' locker for %1
-02:01=深海阎王正在等待 %1
-
-; Round starts
-; 02:02=Let's fight!
-02:02=开战吧!
-; 02:02=Armed and ready!
-02:02=武装准备!
-;02:02=Let's get ready to rumble!
-02:02=准备对轰!
-;02:02=Let's get it on!
-02:02=为胜利而战吧!
-;02:02=Let's get this party started
-02:02=让我们开始这个派对吧
-;02:02=Last hog standing wins
-02:02=胜利属于最后一个生还者
-;02:02=Let's go!
-02:02=出发吧!
-;02:02=Let's rock!
-02:02=一起摇滚吧!
-;02:02=Let's jam!
-;02:02=It's beginning...
-02:02=开始了...
-;02:02=This is the start of something big
-02:02=这是一个伟大的开始
-;02:02=Welcome to Hedgewars
-02:02=欢迎来到刺猬大作战
-;02:02=Welcome to the front lines
-02:02=欢迎来到前线
-;02:02=Crush your enemies!
-02:02=目标:粉碎你的敌人!
-;02:02=May the best hog win
-02:02=祝愿胜利属于最厉害的刺猬!
-;02:02=Victory or death
-02:02=胜利或死亡
-;02:02=To the victor goes the spoils
-02:02=战利品只属于胜利者
-;02:02=Losing is not an option
-02:02=字典里面应该没有"输"这个字的
-;02:02=Cry havoc! Let loose the hogs of war!
-02:02=放声喊吧! 这是刺猬的战争!
-;02:02=Hedgewars, brought to you by Hedgewars.org
-02:02=欢迎来到刺猬大作战, Hedgewars.org 为你呈现
-02:02=GL HF
-;02:02=Just count yourself lucky you're not up against Tiyuri
-02:02=你看你多幸运不是在对战 Tiyuri
-;02:02=Just count yourself lucky you're not up against unC0Rr
-02:02=你看你多幸运不是在对战 unC0Rr
-;02:02=Just count yourself lucky you're not up against Nemo
-02:02=你看你多幸运不是在对战 Nemo
-;02:02=Just count yourself lucky you're not up against Smaxx
-02:02=你看你多幸运不是在对战 Smaxx
-;02:02=Just count yourself lucky you're not up against Jessor
-02:02=你看你多幸运不是在对战 Jessor
-;02:02=Give it your all!
-02:02=展现你的一切吧!
-;02:02=The losers do the cleaning up!
-02:02=输的要罚扫厕所!
-;02:02=Let the fight of the millenium begin
-02:02=宇宙之战开始了
-;02:02=Let the fight of the century begin
-02:02=世纪之战开始了
-;02:02=Let the fight of the decade begin
-02:02=正义之战开始了
-;02:02=Let the fight of the year begin
-02:02=年度争霸战开始了
-;02:02=Let the fight of the month begin
-02:02=本月之星争霸战开始了
-;02:02=Let the fight of the week begin
-02:02=每周擂主争霸战开始了
-;02:02=Let the fight of the day begin
-02:02=本日最强入围赛开始了
-;02:02=Let the fight of the hour begin
-02:02=我们能战一小时!
-;02:02=Do your best!
-02:02=诸君努力!
-;02:02=Destroy the enemy!
-02:02=目标: 摧毁敌人
-;02:02=Good luck
-02:02=祝你好运
-;02:02=Have fun~
-02:02=开心玩~
-;02:02=Fight the good fight
-02:02=漂亮的战斗
-;02:02=Fight dirty
-02:02=不择手段
-;02:02=Fight with honour
-02:02=满载荣誉而战
-;02:02=Don't give up
-02:02=教练告诉你: 别放弃
-;02:02=Never surrender
-02:02=永不屈服!
-;02:02=Rock 'em and sock 'em!
-02:02=蹂虐对手
-;02:02=Let the fragfest begin!
-02:02=积分赛开始!
-;02:02=I hope you're ready for a tussle!
-02:02=你准备好恶战了么?
-;02:02=Go Go Go!
-02:02=冲冲冲!
-;02:02=Hedgehogs advance!
-02:02=刺猬向前冲!
-;02:02=Bring it to them!
-02:02=炸飞他们!
-;02:02=Have no fear!
-02:02=无所畏惧!
-;02:02=Be brave and conquer
-02:02=敢于征服!
-
-; Round ends (win; unused atm)
-02:03=回合结束(胜利)
-
-; Round ends (draw; unused atm)
-02:04=回合结束(平局)
-
-; New health crate
-;02:05=Incoming aid!
-02:05=医疗包!
-;02:05=Medic!
-02:05=急救包!
-;02:05=First aid from the skies!
-02:05=救援物资空运来了!
-;02:05=A health pack for you
-02:05=你的医疗包到了
-;02:05=Good health.. in box form!
-02:05=生命就在那箱子里!
-;02:05=The doctor calls
-02:05=医生的紧急呼叫
-;02:05=Fresh band-aids!
-02:05=新鲜创可贴!
-;02:05=This will make you feel better
-02:05=吃了这个感觉会好些的...
-;02:05=A Hi-Potion! Whoops wrong game
-02:05=兴奋剂!呃。。。走错地方了
-;02:05=A pick-me-up!
-02:05=万金油!
-;02:05=Grab it
-02:05=捉住它
-;02:05=A healthy snack
-02:05=健康食品
-;02:05=A remedy to pain
-02:05=止痛饼来了
-;02:05=Correct Dosage: as many as you can find!
-02:05=使用方法: 吃得越多越好
-;02:05=Urgent delivery
-02:05=紧急物资
-;02:05=Supplies!
-02:05=补给!
-
-; New ammo crate
-; 02:06=More weapons!
-02:06=武器!
-;02:06=Reinforcements!
-02:06=增援!
-;02:06=Lock and load!
-02:06=准备!
-;02:06=I wonder what weapon is in there?
-02:06=我要的那个会在的吧...
-;02:06=Supplies!
-02:06=补给!
-;02:06=What could be inside?
-02:06=里面会有啥呢?
-;02:06=Christmas comes early in Hedgewars
-02:06=刺猬大作战每天都是圣诞节
-;02:06=A present!
-02:06=礼物送到!
-;02:06=Special delivery!
-02:06=特快专递!
-;02:06=It was a nightmare getting this through customs
-02:06=本局的噩梦来了
-;02:06=Destructive toys from the heavens
-02:06=玩具从天堂掉下来了
-;02:06=Warning! Contents Volatile
-02:06=警告! 内含危险物品
-;02:06=Pick it up or blow it up, choice is yours
-02:06=拿走或打爆, 随你
-;02:06=Goodies!
-02:06=好玩意儿!
-;02:06=Mmmmm Ammo
-02:06=弹药!!!!
-;02:06=A box of destructive power
-02:06=潘朵拉之盒
-;02:06=Airmail!
-02:06=天降之物!
-;02:06=Whatever's in that box, it ain't pizza
-02:06=无论里面是啥, 那肯定不会是软妹子
-;02:06=Get it!
-02:06=拿走它!
-;02:06=Weapon drop incoming
-02:06=武器掉下来了!
-;02:06=Don't let the enemy grab that!
-02:06=别让敌人拿了!
-;02:06=Shiny new toys!
-02:06=新玩具!
-;02:06=A mysterious box!
-02:06=神秘的箱子!
-
-; New utility crate
-; 02:07=Tooltime!
-02:07=工具箱!
-;02:07=This could come in handy...
-02:07=这可能派上用场
-;02:07=Utilities!
-02:07=工具!
-;02:07=Utilise this box
-02:07=工具在这里!
-;02:07=Watch out below
-02:07=快看这里!
-;02:07=More utilities!
-02:07=更多选择更多欢笑, 尽在工具包
-;02:07=Tools for you!
-02:07=一堆工具, 送给你!
-;02:07=This should be good!
-02:07=这看见起来蛮好...
-;02:07=Use this wisely
-02:07=使用这个才是明智的选择
-;02:07=Ooo this box is heavy
-02:07=好重...好重...
-;02:07=You might need this
-02:07=会有用的
-
-; Hog (%1) skips his turn
-; 02:08=%1 is sooo boring...
-02:08=%1 太无聊了...
-;02:08=%1 couldn't be bothered
-02:08=%1 不想被打扰!
-;02:08=%1 is one lazy hog
-02:08=%1 太懒了
-;02:08=%1 is thoughtless
-02:08=%1 太轻率了
-;02:08=%1 gave up
-02:08=%1 放弃了
-;02:08=You snooze you lose, %1
-02:08=不认真你就输了, %1
-;02:08=%1 shamelessly skips
-02:08=%1 无耻的跳过了本回合
-;02:08=%1 is really lazy
-02:08=%1 真的太懒了
-;02:08=%1 needs a little more motivation
-02:08=%1 没有动力了
-;02:08=%1 is a pacifist
-02:08=%1 是和平主义者
-;02:08=%1 has a breather
-02:08=%1 需要喘息一下
-;02:08=%1 has a rest
-02:08=%1 需要休息
-;02:08=%1 chills out
-02:08=%1 发冷了
-;02:08=%1 has no faith in his own abilities
-02:08=%1 做啥都没信心了
-;02:08=%1 decides to do nothing at all
-02:08=%1 决定啥都不做
-;02:08=%1 lets the enemy destroy itself
-02:08=%1 认为敌人会自杀的
-;02:08=%1 would be terrible at parties
-02:08=%1 将会陷入可怕的事件中
-;02:08=%1 hides out
-02:08=%1 说:“你看不到我,你看不到我……”
-;02:08=%1 has decided to pass on this opportunity
-02:08=%1 已经决定放弃这个机会
-;02:08=%1 decides the best thing he can do is...nothing
-02:08=%1 决定他现在最应该做的是......坐着不动
-;02:08=%1 is a big wuss
-02:08=%1 大笨蛋!
-;02:08=Buck Buck Buck, %1 is a chicken
-02:08=%1 是小鸡鸡
-;02:08=%1 is looking a little yellow
-02:08=%1 看来有点印堂发黑
-;02:08=%1 is a coward!
-02:08=%1 是懦夫!
-;02:08=%1 is waiting for sudden death
-02:08=%1 在等待突然死亡模式
-;02:08=%1 is not the fighting type
-02:08=%1 不是战斗系的
-;02:08=%1 is reconsidering his purpose in life
-02:08=%1 正在重新寻找他的人生
-;02:08=%1 was never much of a good shot anyway
-02:08=%1 从来没一次打准的
-;02:08=%1 didn't want to join the army in the first place
-02:08=%1 不想参军
-;02:08=Stop wasting our time, %1
-02:08=别浪费时间了! %1
-;02:08=I'm dissapointed in you, %1
-02:08=我对你失望了, %1
-;02:08=Come on, you can do better than that %1
-02:08=%1 明明就能做的更好的
-;02:08=%1's will has broken
-02:08=%1 会被打飞的
-;02:08=%1 apparently has better things to do
-02:08=%1 显然有更好的事情等着做
-;02:08=%1 is scared stiff
-02:08=%1 怕刺激
-;02:08=%1 has fallen asleep
-02:08=%1 睡着了
-
-; Hog (%1) hurts himself only
-; 02:09=%1 should practice aiming!
-02:09=%1 该练练瞄准了!
-; 02:09=%1 seems to hate himself.
-02:09=%1 似乎看自己很不爽。
-; 02:09=%1 is standing on the wrong side!
-02:09=%1 在表演乌龙!
-; 02:09=%1 makes like an emo
-02:09=%1 以为自己无敌
-; 02:09=%1 was holding his weapon the wrong way around
-02:09=%1 好像把武器拿错方向了
-;02:09=%1 is a little sadistic
-02:09=%1 有点施虐狂
-;02:09=%1 is a masochist
-02:09=%1 是受虐狂
-;02:09=%1 has no instinct of self-preservation
-02:09=%1 根本不会自我保护
-;02:09=%1 messed up
-02:09=%1 乱套了
-;02:09=%1 screwed up
-02:09=%1 搞砸了
-;02:09=That was a poor shot, %1
-02:09=%1 这一发真渣
-;02:09=%1 is a little too careless with dangerous weapons
-02:09=%1 太不小心用那些危险的玩意了
-;02:09=%1 should consider a change of career
-02:09=%1 正在考虑转职
-;02:09=Worst. Shot. Ever!
-02:09=更差! 最差! 非常差!
-;02:09=No no no %1, you shoot at the ENEMY!
-02:09=No no no %1, 你要打敌人!
-;02:09=%1 should only be destroying the enemy
-02:09=%1 应该消灭敌人才对
-;02:09=%1 moves one step closer to suicide
-02:09=%1 正在走向自杀
-;02:09=%1 aids the enemy
-02:09=%1 帮助敌人
-;02:09=That was stupid %1
-02:09= %1 是笨蛋
-;02:09=%1 lives by the mantra of "no pain, no gain"
-02:09=%1 贯彻“不付出,何收获“的原则
-;02:09=%1 is confused
-02:09=%1 思维混乱了
-;02:09=%1 hurt itself in its confusion
-02:09=%1 在混乱中攻击自己
-;02:09=%1 has a knack for embarrassing himself
-02:09=%1 正在为自己尴尬
-;02:09=%1 is a klutz!
-02:09=%1 就是一个笨蛋!
-;02:09=%1 is clumsy
-02:09=%1 笨手笨脚的
-;02:09=%1 shows the enemy what he's capable of
-02:09=%1 展示了自己的能力
-;02:09=%1 can't be expected to be perfect all the time
-02:09=%1 不能每次都完美
-;02:09=Don't worry %1, pobody's nerfect
-02:09=不用担心 %1 , 人都不是完美的
-;02:09=%1 totally did that on purpose
-02:09=%1 这么做真的是有目的
-;02:09=I won't tell anyone if you don't, %1
-02:09=我不会把 %1 的事情到处说的
-;02:09=How embarrassing!
-02:09=何等的失态!
-;02:09=I'm sure nobody saw that %1
-02:09=保证,决没人看到 %1 做什么
-;02:09=%1 needs to review his field manual
-02:09=%1 需要复习说明书
-;02:09=%1's weapon clearly malfunctioned
-02:09=%1 的武器很明显坏了
-
-; Hog shot an home run (using the bat and another hog)
-; 02:10=Home Run!
-02:10=全垒打!
-; 02:10=A bird, a plane, ...
-02:10=一只鸟,一架飞机,...
-; 02:10=That one is out!
-02:10=那一位出界了!
-
-; Hog (%1) has to leave (team is gone)
-02:11=%1 必须上床了
-02:11=%1 玩的过火了,休息一下
-02:11=发射!这位已经被送出去
-02:11=%1 必须走了
-
-; Weapon Categories
-03:00=定时手雷
-03:01=定时手雷
-03:02=弹道武器
-03:03=制导武器
-03:04=枪 (多发子弹)
-03:05=钻孔工具
-03:06=动作
-03:07=移动工具
-03:08=接近式炸弹
-03:09=枪 (多发子弹)
-03:10=BOOM!
-03:11=咚!
-03:12=武术
-03:13=未使用
-03:14=移动工具
-03:15=空投打击
-03:16=空投打击
-03:17=打洞工具
-03:18=工具
-03:19=移动工具
-03:20=动作
-03:21=弹道武器
-03:22=叫我主人!
-03:23=武术 (真的!)
-03:24=蛋糕不是谎言!
-03:25=化妆的诱惑
-03:26=果汁手雷
-03:27=烫手手雷
-03:28=弹道武器
-03:29=弹道武器
-03:30=空投打击
-03:31=遥控飞机(不是玩具!)
-03:32=临时效果
-03:33=临时效果
-03:34=临时效果
-03:35=临时效果
-03:36=临时效果
-03:37=临时效果
-03:38=枪 (多发子弹)
-03:39=移动工具
-03:40=燃烧弹
-;03:41=Huge fan of Squawks
-03:41=粉丝的呼喊
-;03:42=I'm making a note here...
-03:42=我将在此做一记录...
-; the misspelled "Beethoven" is intentional (-> to beat)
-;03:43=Performing Beathoven's deadly sonata
-03:43=正在演奏贝揍芬的死亡奏鸣曲
-;03:44=Best before: 1923
-03:44=此日期前最佳:1923
-;03:45=The power of science
-03:45=科学的力量
-;03:46=Hot Hot Hot!
-03:46=烫烫烫!
-;03:47=Stick these somewhere useful!
-03:47= 呆在有利的地方!
-;03:48=It's Hammer time!
-03:48=大锤威武!
-;03:49=Does what you guess
-03:49=尽情猜想
-;03:50=Moles fan
-03:50=地道战
-
-; Weapon Descriptions (use | as line breaks)
-04:00=使用简单的手榴弹攻击敌人.|定时器倒数到0就会爆炸.|1-5: 设定定时器|攻击键: 按住蓄力.
-04:01=使用集束手雷攻击敌人.|定时器倒数到0就会爆炸并裂开成几块.|1-5: 设定定时器|攻击键: 按住蓄力.
-04:02=使用弹道导弹攻击敌人.|受风力影响.|攻击键: 按住蓄力.
-04:03=发射一个制导导弹攻击所选目标.|如果要精确打击就不要使用全力发射.|光标: 选定目标|攻击键: 按住蓄力.
-04:04=霰弹枪有两排子弹.|因为是霰弹枪所以不一定要对准敌人.|攻击键: 开枪 (两发)
-04:05=向地底出发! 使用他就能在地面|打个洞, 就能去其他地方.|攻击键: 开始/停止打洞
-04:06=闷了? 没法打? 保存体力? 没问题!|跳过这回合就可以了, 懦夫!|攻击键: 跳过回合
-04:07=用绳索就可以去很远的地方.|还能空降到别的刺猬身上丢手榴弹呢.|攻击键: 发射/收回绳索|长跳键: 发射手榴弹或其他武器
-04:08=你能用地雷阻止敌人靠近.|还能静悄悄的放在敌人脚下.|一定要在爆炸前逃离到安全的地方!|攻击键: 把地雷放在你的脚下
-04:09=自我感觉准头不行? |沙漠之鹰有4颗子弹呢.|攻击键: 开枪 (四发)
-04:10=使用强力炸药就是一个明智的选择.|这是最经典的轰炸方式.|攻击键: 把炸药放在你的脚下
-04:11=把敌人打飞, 飞出地图或者飞进水里.|或者把地雷打过去?|攻击键: 敲打你面前的所有东西
-04:12=这就是武术的威力!|致命的气功!|攻击键: 使用升龙拳
-04:13=UNUSED
-04:14=有恐高症? 拿降落伞吧.|他能慢慢的安全的把你带到地面.|攻击键: 展开降落伞
-04:15=呼叫一架飞机轰炸你的敌人.|左/右方向键: 决定攻击方向|光标: 选定目标
-04:16=呼叫一架飞机投下大量地雷.|左/右方向键: 决定攻击方向|光标: 选定目标
-04:17=需要个安全的地方? 使用喷灯为你挖掘一条安全的隧道!|攻击键: 开始/停止挖掘
-04:18=喷灯还不够?还要个更安全的地方?|建造若干条大梁挡住吧.|左/右方向键: 选择梁的方向|光标: 建造
-04:19=适当的时候撤退是比所有的攻击|更安全的选择|光标: 选择传送目标
-04:20=可以让你更换当前使用的刺猬.|攻击键: 启动切换功能
-04:21=用炮弹发射器发射一个手榴弹样|的东西. 在爆炸之后会裂开成小块|攻击键: 全力发射
-04:22=这不只是女王才用的东西!|这鞭子能解决很多问题, 比如说那些|喜欢站在悬崖边上的小屁孩.|攻击键: 鞭打你面前的一切东西
-04:23=自杀式炸弹袭击向来好用!|用你的一条命攻击直线上的一切东西并爆炸.|攻击键: 启动自杀性攻击
-04:24=生日快乐! 嗱, 放下这个蛋糕, 他|就会走到敌人身边然后爆炸.| 而且能贴着地形走.|攻击键: 让蛋糕开始/结束走路
-04:25=使用美人计让敌人向着你这个方向跳|(比如跳进海里).|攻击键: 使用本工具诱惑敌人
-04:26=把这个多汁的西瓜扔向敌人!| 一旦定时器倒数完, 就会|炸成几块更强力的炸弹.|攻击键: 按住蓄力.
-04:27=让地狱的礼花在敌人头上绽放!|这真的是危险品, 使用时候记得原理|爆炸之后还会燃烧好一阵子|攻击键: 按住蓄力.
-04:28=本火箭在发射后将会钻到地里|一旦燃料用完或者打穿地面就会爆炸.|攻击键: 按住蓄力.
-04:29=还记得小时候玩的玻璃球么?|不过这个是炸弹版. 发射大量的小玻|璃球然后爆炸|攻击键: 全力发射|上/下方向键: 发射过程中更换方向
-04:30=呼叫一架飞机空投燃烧弹.|用得好的话会造成巨大伤害.|左/右方向键: 决定攻击方向|光标: 选定目标
-04:31=啊哈, 遥控飞机除了能帮你|收集物品之外. 还能空投炸弹.|攻击键: 飞机起飞/投放炸弹|长跳键: 战场之神|上/下方向键: 控制方向
-04:32=低重力装置能影响更多东西!| 除了跳得更远之外还能让|敌人飞得更远.|攻击键: 激活
-04:33=有时候致命打击还是不够过瘾.|攻击键: 激活
-04:34=你打不到我!|攻击键: 激活
-04:35=时间流逝得很快, 你也知道|刺猬腿短.|攻击键: 激活
-04:36=好吧, 你最后还是承认自己眼神不好.|高科技还是能帮你不少的.|攻击键: 激活
-04:37=不用害怕白天.|这只能本回合有效, 可以把造成的伤害变|成自己的血量 .|攻击键: 激活
-04:38=你也知道狙击枪的威力,|能打比较远的地方.|攻击键: 射击 (2发子弹)
-04:39=驾驶飞碟可以飞到地图上的任何角落.|不过这个东西连发明者都认为很难用.|攻击键: 激活|上/左/右方向键: 向某方向飞|前跳:攻击敌人
-04:40=把地面填满汽油然后....|攻击键: 按住蓄力.
-;04:41=The evidence nature might even top the flying|saucer. Birdy can carry your hog around and|drop eggs on your enemies!|Be quick, as using Birdy eats into your turn|time!|Attack: Activate and drop eggs|Up/Left/Right: Flap in one direction
-04:41=自然的力量胜过飞盘的证据。|鸟儿可以携带刺猬并在敌人头上下蛋!|要快!使用鸟儿会消耗回合时间!|攻击键: 激活鸟儿和下蛋|上/左/右方向键: 向某方向飞
-;04:42=This portable portal device is capable|of instantly transporting you, your enemies,|or your weaponry between two points on the|terrain.|Use it wisely and your campaign will be a...|HUGE SUCCESS!|Attack: Shoot a portal|Switch: Cycle portal colours
-04:42=移动传送装置|迅速传输自己或者敌人或者|你的武器,直接连接|地表的两个不同位置。|如果用的聪明那你的战斗将是一场……|巨大的胜利!|攻击键: 发射一个传送点|切换键: 改变颜色
-;04:43=Make your musical debut an explosive success!|Drop a piano from the heavens, but beware...|someone needs to play it, and that may cost you|your life!|Cursor: Select target region|F1-F9: Play the piano
-04:43=音乐细胞的迸发!|钢琴从天堂降落,带|着演奏者最终回归天堂|光标: 选择目标区域|F1-F9:演奏钢琴
-04:44=这不是奶酪!而是生化武器!|爆炸只有一次,带来的毒害是深远的!|1-5: 设定定时器|攻击键: 按住蓄力
-;04:45=All those physics classes have finally |paid off, launch a devastating Sine |wave at your foes. |Watch out, this weapon packs quite a kick. (This weapon is incomplete)|Attack: Shoot
-04:45=全部物理阶级最终|转化为正弦波动|留心,力是相对的|攻击键: 发射
-;04:46=Cover your foes with sizzling liquid flame.|Heartwarming!|Attack: Activate|Up/Down: Continue aiming|Left/Right: Modify spitting power
-04:46= 用满腔的火焰虐待你的对手吧。|攻击键: 激活|上/下方向键: 改变攻击方向|左/右方向键: 调整喷射距离
-;04:47=Double the fun with two spiky, sneaky, sticky mines.|Set up a chain reaction or defend yourself (or both!)|Attack: Hold to shoot with more power (twice)
-04:47=两次机会双重乐趣,隐蔽且黏着的地雷。|利用脑力造成连锁反应!|攻击键: 按住蓄力(两发)
-;04:48=Why should the moles get all the abuse?|Wacking a hog can be just as fun! A good|blow from this hammer will shave off one|third of a hog's health and plunge them|underground.|Attack: Activate
-04:48=痛扁刺猬:用力一锤|将使中者镶入地表,削减它健康的1/3.|攻击键: 打
-;04:49=Resurrect your friends!|But beware that this also resurrects your foes.|Attack: Keep attack pressed to resurrect slowly|Up: Accelerate resurrection
-04:49=复苏|注意,一视同仁|使用: 按住使用键|上: 提高速率
-
-; Game goal strings
-;05:01=The following rules apply
-05:01= 将应用以下规则
-;05:02=Forts: Defend your fortress; vanquish your enemies!
-05:02= 城堡: 守住你的城堡; 削平你的敌人!
-;05:03=Low Gravity: Watch your step
-05:03= 低重力: 注意脚步
-;05:04=Invulnerability: Hogs are (almost) invulnerable
-05:04=无敌: 刺猬不受伤害
-;05:05=Vampirism: Hogs will be healed for the damage dealt
-05:05=吸血: 敌人失去的就是我的
-;05:06=Karma: Hogs will be damaged for the damage dealt
-05:06=因果效应: 伤害有多少,自己都知道
-;05:07=Protect the King: Don't let your king die!|Place the King: Pick a protected starting point for your King
-05:07=保护国王: 国王不能死!|放置国王: 为国王选择安全的起始地点
-;05:08=Place Hedgehogs: Place your hogs before the game starts
-05:08=选择起始点: 游戏开始前手动放置刺猬
-;05:09=Artillery: Hogs can't walk to change position
-05:09=远程打击: 不许动!
-;05:10=Indestructible Terrain: Most weapons won't destroy terrain
-05:10=无损地表: 多数武器无法改变地形
-;05:11=Shared Ammo: All teams of the same color share their ammunition
-05:11=共享装备: 同色的刺猬共享它们的装备
-;05:12=Mine Timers: Mines will detonate after %1 second(s)
-05:12=地雷定时器: %1 秒起爆
-;05:13=Mine Timers: Mines will detonate instantly
-05:13=地雷定时器: 立即起爆
-;05:14=Mine Timers: Mines will detonate after 0 - 3 seconds
-05:14=地雷定时器: 0-3 秒起爆
-;05:15=Damage Modifier: All weapons will do %1% damage
-05:15=伤害修正: 武器伤害使用 %1% 修正值
-;05:16=Health of all hogs is reset on end of turn
-05:16=所有活着的刺猬回合结尾时彻底恢复健康
-;05:17=AI hogs respawn on death
-05:17=AI刺猬即时复活
-;05:18=Unlimited Attacks
-05:18=无限攻击法则
-;05:19=Weapons are reset on end of turn
-05:19=武器在回合结束时重置
-;05:20=Weapons are not shared between hogs
-05:20=刺猬的武器无法分享
+01:05=退出 (Y/Esc)?
+01:06=出现紧急情况!
--- a/tools/PascalParser.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/PascalParser.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -19,7 +19,7 @@
 
 pascalUnit = do
     comments
-    u <- choice [program, unit, systemUnit]
+    u <- choice [program, unit, systemUnit, redoUnit]
     comments
     return u
 
@@ -270,12 +270,12 @@
         char ';'
         comments
         forward <- liftM isJust $ optionMaybe (try (string "forward;") >> comments)
-        many functionDecorator
+        inline <- liftM (any (== "inline;")) $ many functionDecorator
         b <- if isImpl && (not forward) then
                 liftM Just functionBody
                 else
                 return Nothing
-        return $ [OperatorDeclaration i rid ret vs b]
+        return $ [OperatorDeclaration i rid inline ret vs b]
 
 
     funcDecl = do
@@ -295,21 +295,24 @@
         char ';'
         comments
         forward <- liftM isJust $ optionMaybe (try (string "forward;") >> comments)
-        many functionDecorator
+        inline <- liftM (any (== "inline;")) $ many functionDecorator
         b <- if isImpl && (not forward) then
                 liftM Just functionBody
                 else
                 return Nothing
-        return $ [FunctionDeclaration i ret vs b]
+        return $ [FunctionDeclaration i inline ret vs b]
 
-    functionDecorator = choice [
-        try $ string "inline;"
-        , try $ caseInsensitiveString "cdecl;"
-        , try $ string "overload;"
-        , try $ string "export;"
-        , try $ string "varargs;"
-        , try (string "external") >> comments >> iD >> optional (string "name" >> comments >> stringLiteral pas)>> string ";"
-        ] >> comments
+    functionDecorator = do
+        d <- choice [
+            try $ string "inline;"
+            , try $ caseInsensitiveString "cdecl;"
+            , try $ string "overload;"
+            , try $ string "export;"
+            , try $ string "varargs;"
+            , try (string "external") >> comments >> iD >> optional (string "name" >> comments >> stringLiteral pas)>> string ";"
+            ]
+        comments
+        return d
 
 
 program = do
@@ -348,36 +351,46 @@
     comments
     return $ Implementation u (TypesAndVars tv)
 
-expression = buildExpressionParser table term <?> "expression"
+expression = do
+    buildExpressionParser table term <?> "expression"
     where
     term = comments >> choice [
         builtInFunction expression >>= \(n, e) -> return $ BuiltInFunCall e (SimpleReference (Identifier n BTUnknown))
         , try (parens pas $ expression >>= \e -> notFollowedBy (comments >> char '.') >> return e)
         , brackets pas (commaSep pas iD) >>= return . SetExpression
-        , try $ natural pas >>= \i -> notFollowedBy (char '.') >> (return . NumberLiteral . show) i
+        , try $ integer pas >>= \i -> notFollowedBy (char '.') >> (return . NumberLiteral . show) i
         , float pas >>= return . FloatLiteral . show
-        , natural pas >>= return . NumberLiteral . show
+        , try $ integer pas >>= return . NumberLiteral . show
         , try (string "_S" >> stringLiteral pas) >>= return . StringLiteral
         , try (string "_P" >> stringLiteral pas) >>= return . PCharLiteral
         , stringLiteral pas >>= return . strOrChar
         , try (string "#$") >> many hexDigit >>= \c -> comments >> return (HexCharCode c)
         , char '#' >> many digit >>= \c -> comments >> return (CharCode c)
         , char '$' >> many hexDigit >>=  \h -> comments >> return (HexNumber h)
-        , char '-' >> expression >>= return . PrefixOp "-"
+        --, char '-' >> expression >>= return . PrefixOp "-"
+        , char '-' >> reference >>= return . PrefixOp "-" . Reference
+        , try $ string "not" >> error "unexpected not in term"
         , try $ string "nil" >> return Null
-        , try $ string "not" >> expression >>= return . PrefixOp "not"
         , reference >>= return . Reference
         ] <?> "simple expression"
 
-    table = [ 
+    table = [
+          [  Prefix (try (string "not") >> return (PrefixOp "not"))
+           , Prefix (try (char '-') >> return (PrefixOp "-"))]
+        ,
           [  Infix (char '*' >> return (BinOp "*")) AssocLeft
            , Infix (char '/' >> return (BinOp "/")) AssocLeft
            , Infix (try (string "div") >> return (BinOp "div")) AssocLeft
            , Infix (try (string "mod") >> return (BinOp "mod")) AssocLeft
            , Infix (try (string "in") >> return (BinOp "in")) AssocNone
+           , Infix (try $ string "and" >> return (BinOp "and")) AssocLeft
+           , Infix (try $ string "shl" >> return (BinOp "shl")) AssocLeft
+           , Infix (try $ string "shr" >> return (BinOp "shr")) AssocLeft
           ]
         , [  Infix (char '+' >> return (BinOp "+")) AssocLeft
            , Infix (char '-' >> return (BinOp "-")) AssocLeft
+           , Infix (try $ string "or" >> return (BinOp "or")) AssocLeft
+           , Infix (try $ string "xor" >> return (BinOp "xor")) AssocLeft
           ]
         , [  Infix (try (string "<>") >> return (BinOp "<>")) AssocNone
            , Infix (try (string "<=") >> return (BinOp "<=")) AssocNone
@@ -385,13 +398,13 @@
            , Infix (char '<' >> return (BinOp "<")) AssocNone
            , Infix (char '>' >> return (BinOp ">")) AssocNone
           ]
-        , [  Infix (try $ string "shl" >> return (BinOp "shl")) AssocNone
-           , Infix (try $ string "shr" >> return (BinOp "shr")) AssocNone
+        {-, [  Infix (try $ string "shl" >> return (BinOp "shl")) AssocNone
+             , Infix (try $ string "shr" >> return (BinOp "shr")) AssocNone
           ]
-        , [  Infix (try $ string "and" >> return (BinOp "and")) AssocLeft
-           , Infix (try $ string "or" >> return (BinOp "or")) AssocLeft
+        , [ 
+             Infix (try $ string "or" >> return (BinOp "or")) AssocLeft
            , Infix (try $ string "xor" >> return (BinOp "xor")) AssocLeft
-          ]
+          ]-}
         , [
              Infix (char '=' >> return (BinOp "=")) AssocNone
           ]
@@ -415,7 +428,7 @@
         , switchCase
         , withBlock
         , forCycle
-        , (try $ reference >>= \r -> string ":=" >> return r) >>= \r -> expression >>= return . Assignment r
+        , (try $ reference >>= \r -> string ":=" >> return r) >>= \r -> comments >> expression >>= return . Assignment r
         , builtInFunction expression >>= \(n, e) -> return $ BuiltInFunctionCall e (SimpleReference (Identifier n BTUnknown))
         , procCall
         , char ';' >> comments >> return NOP
@@ -480,7 +493,12 @@
     comments
     e1 <- expression
     comments
-    choice [string "to", string "downto"]
+    up <- liftM (== Just "to") $
+            optionMaybe $ choice [
+                try $ string "to"
+                , try $ string "downto"
+                ]   
+    --choice [string "to", string "downto"]
     comments
     e2 <- expression
     comments
@@ -488,7 +506,7 @@
     comments
     p <- phrase
     comments
-    return $ ForCycle i e1 e2 p
+    return $ ForCycle i e1 e2 p up
 
 switchCase = do
     try $ string "case"
@@ -573,14 +591,20 @@
     table = [
           [
              Prefix (char '-' >> return (InitPrefixOp "-"))
+            ,Prefix (try (string "not") >> return (InitPrefixOp "not"))
           ]
         , [  Infix (char '*' >> return (InitBinOp "*")) AssocLeft
            , Infix (char '/' >> return (InitBinOp "/")) AssocLeft
            , Infix (try (string "div") >> return (InitBinOp "div")) AssocLeft
            , Infix (try (string "mod") >> return (InitBinOp "mod")) AssocLeft
+           , Infix (try $ string "and" >> return (InitBinOp "and")) AssocLeft
+           , Infix (try $ string "shl" >> return (InitBinOp "shl")) AssocNone
+           , Infix (try $ string "shr" >> return (InitBinOp "shr")) AssocNone
           ]
         , [  Infix (char '+' >> return (InitBinOp "+")) AssocLeft
            , Infix (char '-' >> return (InitBinOp "-")) AssocLeft
+           , Infix (try $ string "or" >> return (InitBinOp "or")) AssocLeft
+           , Infix (try $ string "xor" >> return (InitBinOp "xor")) AssocLeft
           ]
         , [  Infix (try (string "<>") >> return (InitBinOp "<>")) AssocNone
            , Infix (try (string "<=") >> return (InitBinOp "<=")) AssocNone
@@ -589,14 +613,14 @@
            , Infix (char '>' >> return (InitBinOp ">")) AssocNone
            , Infix (char '=' >> return (InitBinOp "=")) AssocNone
           ]
-        , [  Infix (try $ string "and" >> return (InitBinOp "and")) AssocLeft
+        {--, [  Infix (try $ string "and" >> return (InitBinOp "and")) AssocLeft
            , Infix (try $ string "or" >> return (InitBinOp "or")) AssocLeft
            , Infix (try $ string "xor" >> return (InitBinOp "xor")) AssocLeft
           ]
         , [  Infix (try $ string "shl" >> return (InitBinOp "shl")) AssocNone
            , Infix (try $ string "shr" >> return (InitBinOp "shr")) AssocNone
-          ]
-        , [Prefix (try (string "not") >> return (InitPrefixOp "not"))]
+          ]--}
+        --, [Prefix (try (string "not") >> return (InitPrefixOp "not"))]
         ]
 
     itypeCast = do
@@ -621,3 +645,14 @@
     string "var"
     v <- varsDecl True
     return $ System (t ++ v)
+
+redoUnit = do
+    string "redo;"
+    comments
+    string "type"
+    comments
+    t <- typesDecl
+    string "var"
+    v <- varsDecl True
+    return $ Redo (t ++ v)
+
--- a/tools/PascalPreprocessor.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/PascalPreprocessor.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -18,6 +18,8 @@
 initDefines = Map.fromList [
     ("FPC", "")
     , ("PAS2C", "")
+    , ("ENDIAN_LITTLE", "")
+    , ("S3D_DISABLED", "")
     ]
 
 preprocess :: String -> IO String
--- a/tools/PascalUnitSyntaxTree.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/PascalUnitSyntaxTree.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -7,6 +7,7 @@
     Program Identifier Implementation Phrase
     | Unit Identifier Interface Implementation (Maybe Initialize) (Maybe Finalize)
     | System [TypeVarDeclaration]
+    | Redo [TypeVarDeclaration]
     deriving Show
 data Interface = Interface Uses TypesAndVars
     deriving Show
@@ -18,8 +19,8 @@
     deriving Show
 data TypeVarDeclaration = TypeDeclaration Identifier TypeDecl
     | VarDeclaration Bool Bool ([Identifier], TypeDecl) (Maybe InitExpression)
-    | FunctionDeclaration Identifier TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
-    | OperatorDeclaration String Identifier TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
+    | FunctionDeclaration Identifier Bool TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
+    | OperatorDeclaration String Identifier Bool TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
     deriving Show
 data TypeDecl = SimpleType Identifier
     | RangeType Range
@@ -48,7 +49,7 @@
         | IfThenElse Expression Phrase (Maybe Phrase)
         | WhileCycle Expression Phrase
         | RepeatCycle Expression [Phrase]
-        | ForCycle Identifier Expression Expression Phrase
+        | ForCycle Identifier Expression Expression Phrase Bool -- The last Boolean indicates wether it's up or down counting
         | WithBlock Reference Phrase
         | Phrases [Phrase]
         | SwitchCase Expression [([InitExpression], Phrase)] (Maybe [Phrase])
--- a/tools/pas2c.hs	Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/pas2c.hs	Thu Aug 30 13:02:19 2012 -0400
@@ -17,24 +17,32 @@
 import Data.List (find)
 import Numeric
 
-import PascalParser
+import PascalParser(pascalUnit)
 import PascalUnitSyntaxTree
 
 
 data InsertOption =
     IOInsert
+    | IOInsertWithType Doc
     | IOLookup
     | IOLookupLast
     | IOLookupFunction Int
     | IODeferred
 
-type Record = (String, BaseType)
+data Record = Record
+    {
+        lcaseId :: String,
+        baseType :: BaseType,
+        typeDecl :: Doc
+    }
+    deriving Show
 type Records = Map.Map String [Record]
 data RenderState = RenderState
     {
         currentScope :: Records,
         lastIdentifier :: String,
         lastType :: BaseType,
+        lastIdTypeDecl :: Doc,
         stringConsts :: [(String, String)],
         uniqCounter :: Int,
         toMangle :: Set.Set String,
@@ -43,7 +51,9 @@
         namespaces :: Map.Map String Records
     }
 
-emptyState = RenderState Map.empty "" BTUnknown [] 0 Set.empty "" ""
+rec2Records = map (\(a, b) -> Record a b empty)
+
+emptyState = RenderState Map.empty "" BTUnknown empty [] 0 Set.empty "" ""
 
 getUniq :: State RenderState Int
 getUniq = do
@@ -71,13 +81,14 @@
 
 escapeChar :: Char -> ShowS
 escapeChar '"' s = "\\\"" ++ s
+escapeChar '\\' s = "\\\\" ++ s
 escapeChar a s = a : s
 
 strInit :: String -> Doc
 strInit a = text "STRINIT" <> parens (doubleQuotes (text $ escapeStr a))
 
 renderStringConsts :: State RenderState Doc
-renderStringConsts = liftM (vcat . map (\(a, b) -> text "const string255" <+> (text a) <+> text "=" <+> strInit b <> semi))
+renderStringConsts = liftM (vcat . map (\(a, b) -> text "static const string255" <+> (text a) <+> text "=" <+> strInit b <> semi))
     $ gets stringConsts
 
 docToLower :: Doc -> Doc
@@ -132,10 +143,16 @@
         where
         f = do
             checkDuplicateFunDecls tvs
-            mapM_ (tvar2C True) tvs
+            mapM_ (tvar2C True False True False) tvs
+    toNamespace nss (Redo tvs) = -- functions that are re-implemented, add prefix to all of them
+        currentScope $ execState f (emptyState nss){currentUnit = "fpcrtl_"}
+        where
+        f = do
+            checkDuplicateFunDecls tvs
+            mapM_ (tvar2C True False True False) tvs
     toNamespace _ (Program {}) = Map.empty
     toNamespace nss (Unit (Identifier i _) interface _ _ _) =
-        currentScope $ execState (interface2C interface) (emptyState nss){currentUnit = map toLower i ++ "_"}
+        currentScope $ execState (interface2C interface True) (emptyState nss){currentUnit = map toLower i ++ "_"}
 
 
 withState' :: (RenderState -> RenderState) -> State RenderState a -> State RenderState a
@@ -149,65 +166,72 @@
         })
     return a
 
-withLastIdNamespace :: State RenderState Doc -> State RenderState Doc
 withLastIdNamespace f = do
     li <- gets lastIdentifier
     nss <- gets namespaces
     withState' (\st -> st{currentScope = fromMaybe Map.empty $ Map.lookup li (namespaces st)}) f
 
-withRecordNamespace :: String -> [(String, BaseType)] -> State RenderState Doc -> State RenderState Doc
+withRecordNamespace :: String -> [Record] -> State RenderState Doc -> State RenderState Doc
 withRecordNamespace _ [] = error "withRecordNamespace: empty record"
 withRecordNamespace prefix recs = withState' f
     where
         f st = st{currentScope = Map.unionWith un records (currentScope st), currentUnit = ""}
-        records = Map.fromList $ map (\(a, b) -> (map toLower a, [(prefix ++ a, b)])) recs
+        records = Map.fromList $ map (\(Record a b d) -> (map toLower a, [Record (prefix ++ a) b d])) recs
         un [a] b = a : b
 
 toCFiles :: Map.Map String Records -> (String, PascalUnit) -> IO ()
 toCFiles _ (_, System _) = return ()
+toCFiles _ (_, Redo _) = return ()
 toCFiles ns p@(fn, pu) = do
     hPutStrLn stdout $ "Rendering '" ++ fn ++ "'..."
     toCFiles' p
     where
-    toCFiles' (fn, p@(Program {})) = writeFile (fn ++ ".c") $ (render2C initialState . pascal2C) p
+    toCFiles' (fn, p@(Program {})) = writeFile (fn ++ ".c") $ "#include \"fpcrtl.h\"\n" ++ (render2C initialState . pascal2C) p
     toCFiles' (fn, (Unit unitId@(Identifier i _) interface implementation _ _)) = do
-        let (a, s) = runState (id2C IOInsert (setBaseType BTUnit unitId) >> interface2C interface) initialState{currentUnit = map toLower i ++ "_"}
+        let (a, s) = runState (id2C IOInsert (setBaseType BTUnit unitId) >> interface2C interface True) initialState{currentUnit = map toLower i ++ "_"}
+            (a', s') = runState (id2C IOInsert (setBaseType BTUnit unitId) >> interface2C interface False) initialState{currentUnit = map toLower i ++ "_"}
         writeFile (fn ++ ".h") $ "#pragma once\n\n#include \"pas2c.h\"\n\n" ++ (render (a $+$ text ""))
-        writeFile (fn ++ ".c") $ "#include \"" ++ fn ++ ".h\"\n" ++ (render2C s . implementation2C) implementation
+        writeFile (fn ++ ".c") $ "#include \"fpcrtl.h\"\n\n#include \"" ++ fn ++ ".h\"\n" ++ render (a' $+$ text "") ++ (render2C s . implementation2C) implementation
     initialState = emptyState ns
 
     render2C :: RenderState -> State RenderState Doc -> String
     render2C a = render . ($+$ empty) . flip evalState a
 
+
 usesFiles :: PascalUnit -> [String]
-usesFiles (Program _ (Implementation uses _) _) = "pas2cSystem" : uses2List uses
-usesFiles (Unit _ (Interface uses1 _) (Implementation uses2 _) _ _) = "pas2cSystem" : uses2List uses1 ++ uses2List uses2
+usesFiles (Program _ (Implementation uses _) _) = ["pas2cSystem", "pas2cRedo"] ++ uses2List uses
+usesFiles (Unit _ (Interface uses1 _) (Implementation uses2 _) _ _) = ["pas2cSystem", "pas2cRedo"] ++ uses2List uses1 ++ uses2List uses2
 usesFiles (System {}) = []
-
+usesFiles (Redo {}) = []
 
 pascal2C :: PascalUnit -> State RenderState Doc
 pascal2C (Unit _ interface implementation init fin) =
-    liftM2 ($+$) (interface2C interface) (implementation2C implementation)
+    liftM2 ($+$) (interface2C interface True) (implementation2C implementation)
 
 pascal2C (Program _ implementation mainFunction) = do
     impl <- implementation2C implementation
-    [main] <- tvar2C True
-        (FunctionDeclaration (Identifier "main" BTInt) (SimpleType $ Identifier "int" BTInt) [] (Just (TypesAndVars [], mainFunction)))
+    [main] <- tvar2C True False True True (FunctionDeclaration (Identifier "main" BTInt) False (SimpleType $ Identifier "int" BTInt) [VarDeclaration False False ([Identifier "argc" BTInt], SimpleType (Identifier "Integer" BTInt)) Nothing, VarDeclaration False False ([Identifier "argv" BTUnknown], SimpleType (Identifier "PPChar" BTUnknown)) Nothing] (Just (TypesAndVars [], mainFunction)))
     return $ impl $+$ main
 
 
-
-interface2C :: Interface -> State RenderState Doc
-interface2C (Interface uses tvars) = do
+-- the second bool indicates whether do normal interface translation or generate variable declarations
+-- that will be inserted into implementation files
+interface2C :: Interface -> Bool -> State RenderState Doc
+interface2C (Interface uses tvars) True = do
     u <- uses2C uses
-    tv <- typesAndVars2C True tvars
+    tv <- typesAndVars2C True True True tvars
     r <- renderStringConsts
     return (u $+$ r $+$ tv)
+interface2C (Interface uses tvars) False = do
+    u <- uses2C uses
+    tv <- typesAndVars2C True False False tvars
+    r <- renderStringConsts
+    return tv
 
 implementation2C :: Implementation -> State RenderState Doc
 implementation2C (Implementation uses tvars) = do
     u <- uses2C uses
-    tv <- typesAndVars2C True tvars
+    tv <- typesAndVars2C True False True tvars
     r <- renderStringConsts
     return (u $+$ r $+$ tv)
 
@@ -217,20 +241,25 @@
     where
         initMap = Map.empty
         --initMap = Map.fromList [("reset", 2)]
-        ins (FunctionDeclaration (Identifier i _) _ _ _) m = Map.insertWith (+) (map toLower i) 1 m
+        ins (FunctionDeclaration (Identifier i _) _ _ _ _) m = Map.insertWith (+) (map toLower i) 1 m
         ins _ m = m
 
-typesAndVars2C :: Bool -> TypesAndVars -> State RenderState Doc
-typesAndVars2C b (TypesAndVars ts) = do
+-- the second bool indicates whether declare variable as extern or not
+-- the third bool indicates whether include types or not
+
+typesAndVars2C :: Bool -> Bool -> Bool -> TypesAndVars -> State RenderState Doc
+typesAndVars2C b externVar includeType(TypesAndVars ts) = do
     checkDuplicateFunDecls ts
-    liftM (vcat . map (<> semi) . concat) $ mapM (tvar2C b) ts
+    liftM (vcat . map (<> semi) . concat) $ mapM (tvar2C b externVar includeType False) ts
 
 setBaseType :: BaseType -> Identifier -> Identifier
 setBaseType bt (Identifier i _) = Identifier i bt
 
 uses2C :: Uses -> State RenderState Doc
 uses2C uses@(Uses unitIds) = do
+
     mapM_ injectNamespace (Identifier "pas2cSystem" undefined : unitIds)
+    mapM_ injectNamespace (Identifier "pas2cRedo" undefined : unitIds)
     mapM_ (id2C IOInsert . setBaseType BTUnit) unitIds
     return $ vcat . map (\i -> text $ "#include \"" ++ i ++ ".h\"") $ uses2List uses
     where
@@ -242,8 +271,11 @@
 uses2List (Uses ids) = map (\(Identifier i _) -> i) ids
 
 
+setLastIdValues vv = (\s -> s{lastType = baseType vv, lastIdentifier = lcaseId vv, lastIdTypeDecl = typeDecl vv})
+
 id2C :: InsertOption -> Identifier -> State RenderState Doc
-id2C IOInsert (Identifier i t) = do
+id2C IOInsert i = id2C (IOInsertWithType empty) i
+id2C (IOInsertWithType d) (Identifier i t) = do
     ns <- gets currentScope
     tom <- gets (Set.member n . toMangle)
     cu <- gets currentUnit
@@ -252,10 +284,11 @@
             (BTFunction _ _ _, _) -> (cu ++ i, t)
             (BTVarParam t', _) -> ('(' : '*' : i ++ ")" , t')
             _ -> (i, t)
-    modify (\s -> s{currentScope = Map.insertWith (++) n [(i', t')] (currentScope s), lastIdentifier = n})
+    modify (\s -> s{currentScope = Map.insertWith (++) n [Record i' t' d] (currentScope s), lastIdentifier = n})
     return $ text i'
     where
         n = map toLower i
+
 id2C IOLookup i = id2CLookup head i
 id2C IOLookupLast i = id2CLookup last i
 id2C (IOLookupFunction params) (Identifier i t) = do
@@ -266,9 +299,9 @@
         error $ "Not defined: '" ++ i' ++ "'\n" ++ show lt ++ "\nwith num of params = " ++ show params ++ "\n" ++ show v
         else
         let vv = fromMaybe (head $ fromJust v) . find checkParam $ fromJust v in
-            modify (\s -> s{lastType = snd vv, lastIdentifier = fst vv}) >> (return . text . fst $ vv)
+            modify (setLastIdValues vv) >> (return . text . lcaseId $ vv)
     where
-        checkParam (_, BTFunction _ p _) = p == params
+        checkParam (Record _ (BTFunction _ p _) _) = p == params
         checkParam _ = False
 id2C IODeferred (Identifier i t) = do
     let i' = map toLower i
@@ -276,40 +309,44 @@
     if (isNothing v) then
         modify (\s -> s{lastType = BTUnknown, lastIdentifier = i}) >> return (text i)
         else
-        let vv = head $ fromJust v in modify (\s -> s{lastType = snd vv, lastIdentifier = fst vv}) >> (return . text . fst $ vv)
+        let vv = head $ fromJust v in modify (setLastIdValues vv) >> (return . text . lcaseId $ vv)
 
 id2CLookup :: ([Record] -> Record) -> Identifier -> State RenderState Doc
-id2CLookup f (Identifier i _) = do
+id2CLookup f (Identifier i t) = do
     let i' = map toLower i
     v <- gets $ Map.lookup i' . currentScope
     lt <- gets lastType
     if isNothing v then
         error $ "Not defined: '" ++ i' ++ "'\n" ++ show lt
         else
-        let vv = f $ fromJust v in modify (\s -> s{lastType = snd vv, lastIdentifier = fst vv}) >> (return . text . fst $ vv)
+        let vv = f $ fromJust v in modify (setLastIdValues vv) >> (return . text . lcaseId $ vv)
 
 
 id2CTyped :: TypeDecl -> Identifier -> State RenderState Doc
-id2CTyped t (Identifier i _) = do
+id2CTyped = id2CTyped2 Nothing
+
+id2CTyped2 :: Maybe Doc -> TypeDecl -> Identifier -> State RenderState Doc
+id2CTyped2 md t (Identifier i _) = do
     tb <- resolveType t
     case (t, tb) of
         (_, BTUnknown) -> do
             error $ "id2CTyped: type BTUnknown for " ++ show i ++ "\ntype: " ++ show t
         (SimpleType {}, BTRecord _ r) -> do
             ts <- type2C t
-            id2C IOInsert (Identifier i (BTRecord (render $ ts empty) r))
+            id2C (IOInsertWithType $ ts empty) (Identifier i (BTRecord (render $ ts empty) r))
         (_, BTRecord _ r) -> do
             ts <- type2C t
-            id2C IOInsert (Identifier i (BTRecord i r))
-        _ -> id2C IOInsert (Identifier i tb)
-
+            id2C (IOInsertWithType $ ts empty) (Identifier i (BTRecord i r))
+        _ -> case md of
+                Nothing -> id2C IOInsert (Identifier i tb)
+                Just ts -> id2C (IOInsertWithType ts) (Identifier i tb)
 
 
 resolveType :: TypeDecl -> State RenderState BaseType
 resolveType st@(SimpleType (Identifier i _)) = do
     let i' = map toLower i
     v <- gets $ Map.lookup i' . currentScope
-    if isJust v then return . snd . head $ fromJust v else return $ f i'
+    if isJust v then return . baseType . head $ fromJust v else return $ f i'
     where
     f "integer" = BTInt
     f "pointer" = BTPointerTo BTVoid
@@ -352,7 +389,7 @@
 resolve s (BTUnresolved t) = do
     v <- gets $ Map.lookup t . currentScope
     if isJust v then
-        resolve s . snd . head . fromJust $ v
+        resolve s . baseType . head . fromJust $ v
         else
         error $ "Unknown type " ++ show t ++ "\n" ++ s
 resolve _ t = return t
@@ -363,7 +400,7 @@
     error $ "Dereferencing from non-pointer type " ++ show t ++ "\n" ++ s
 
 
-functionParams2C params = liftM (hcat . punctuate comma . concat) $ mapM (tvar2C False) params
+functionParams2C params = liftM (hcat . punctuate comma . concat) $ mapM (tvar2C False False True True) params
 
 numberOfDeclarations :: [TypeVarDeclaration] -> Int
 numberOfDeclarations = sum . map cnt
@@ -392,20 +429,21 @@
         ps = zip ['a'..] (toIsVarList params)
 
 fun2C :: Bool -> String -> TypeVarDeclaration -> State RenderState [Doc]
-fun2C _ _ (FunctionDeclaration name returnType params Nothing) = do
+fun2C _ _ (FunctionDeclaration name inline returnType params Nothing) = do
     t <- type2C returnType
     t'<- gets lastType
     p <- withState' id $ functionParams2C params
     n <- liftM render . id2C IOInsert $ setBaseType (BTFunction hasVars (numberOfDeclarations params) t') name
+    let decor = if inline then text "inline" else empty
     if hasVars then
-        return [funWithVarsToDefine n params $+$ t empty <+> text (n ++ "__vars") <> parens p]
+        return [funWithVarsToDefine n params $+$ decor <+> t empty <+> text (n ++ "__vars") <> parens p]
         else
-        return [t empty <+> text n <> parens p]
+        return [decor <+> t empty <+> text n <> parens p]
     where
         hasVars = hasPassByReference params
 
 
-fun2C True rv (FunctionDeclaration name@(Identifier i _) returnType params (Just (tvars, phrase))) = do
+fun2C True rv (FunctionDeclaration name@(Identifier i _) inline returnType params (Just (tvars, phrase))) = do
     let res = docToLower $ text rv <> text "_result"
     t <- type2C returnType
     t'<- gets lastType
@@ -418,16 +456,20 @@
             VoidType -> True
             _ -> False
 
-    (p, ph) <- withState' (\st -> st{currentScope = Map.insertWith un (map toLower rv) [(render res, t')] $ currentScope st
+    (p, ph) <- withState' (\st -> st{currentScope = Map.insertWith un (map toLower rv) [Record (render res) t' empty] $ currentScope st
             , currentFunctionResult = if isVoid then [] else render res}) $ do
         p <- functionParams2C params
-        ph <- liftM2 ($+$) (typesAndVars2C False tvars) (phrase2C' phrase)
+        ph <- liftM2 ($+$) (typesAndVars2C False False True tvars) (phrase2C' phrase)
         return (p, ph)
 
     let phrasesBlock = if isVoid then ph else t empty <+> res <> semi $+$ ph $+$ text "return" <+> res <> semi
-
-    return [(if notDeclared && hasVars then funWithVarsToDefine n params else empty) $+$
-        t empty <+> text (if hasVars then n ++ "__vars" else n) <> parens p
+    let define = if hasVars then text "#ifndef" <+> text n $+$ funWithVarsToDefine n params $+$ text "#endif" else empty
+    let decor = if inline then text "inline" else empty
+    return [
+        define
+        $+$
+        --(if notDeclared && hasVars then funWithVarsToDefine n params else empty) $+$
+        decor <+> t empty <+> text (if hasVars then n ++ "__vars" else n) <> parens p
         $+$
         text "{"
         $+$
@@ -440,42 +482,74 @@
     un [a] b = a : b
     hasVars = hasPassByReference params
 
-fun2C False _ (FunctionDeclaration (Identifier name _) _ _ _) = error $ "nested functions not allowed: " ++ name
+fun2C False _ (FunctionDeclaration (Identifier name _) _ _ _ _) = error $ "nested functions not allowed: " ++ name
 fun2C _ tv _ = error $ "fun2C: I don't render " ++ show tv
 
-tvar2C :: Bool -> TypeVarDeclaration -> State RenderState [Doc]
-tvar2C b f@(FunctionDeclaration (Identifier name _) _ _ _) =
-    fun2C b name f
-tvar2C _ td@(TypeDeclaration i' t) = do
+-- the second bool indicates whether declare variable as extern or not
+-- the third bool indicates whether include types or not
+-- the fourth bool indicates whether ignore initialization or not (basically for dynamic arrays since we cannot do initialization in function params)
+tvar2C :: Bool -> Bool -> Bool -> Bool -> TypeVarDeclaration -> State RenderState [Doc]
+tvar2C b _ includeType _ f@(FunctionDeclaration (Identifier name _) _ _ _ _) = do
+    t <- fun2C b name f
+    if includeType then return t else return []
+tvar2C _ _ includeType _ td@(TypeDeclaration i' t) = do
     i <- id2CTyped t i'
     tp <- type2C t
-    return [text "typedef" <+> tp i]
+    return $ if includeType then [text "typedef" <+> tp i] else []
 
-tvar2C _ (VarDeclaration True _ (ids, t) Nothing) = do
+tvar2C _ _ _ _ (VarDeclaration True _ (ids, t) Nothing) = do
     t' <- liftM ((empty <+>) . ) $ type2C t
-    liftM (map(\i -> t' i)) $ mapM (id2CTyped (VarParamType t)) ids
+    liftM (map(\i -> t' i)) $ mapM (id2CTyped2 (Just $ t' empty) (VarParamType t)) ids
 
-tvar2C _ (VarDeclaration _ isConst (ids, t) mInitExpr) = do
-    t' <- liftM (((if isConst then text "const" else empty) <+>) . ) $ type2C t
+tvar2C _ externVar includeType ignoreInit (VarDeclaration _ isConst (ids, t) mInitExpr) = do
+    t' <- liftM (((if isConst then text "static const" else if externVar 
+                                                                then text "extern"
+                                                                else empty)
+                   <+>) . ) $ type2C t
     ie <- initExpr mInitExpr
     lt <- gets lastType
     case (isConst, lt, ids, mInitExpr) of
          (True, BTInt, [i], Just _) -> do
              i' <- id2CTyped t i
-             return [text "enum" <> braces (i' <+> ie)]
+             return $ if includeType then [text "enum" <> braces (i' <+> ie)] else []
          (True, BTFloat, [i], Just e) -> do
              i' <- id2CTyped t i
              ie <- initExpr2C e
-             return [text "#define" <+> i' <+> parens ie <> text "\n"]
+             return $ if includeType then [text "#define" <+> i' <+> parens ie <> text "\n"] else []
          (_, BTFunction{}, _, Nothing) -> liftM (map(\i -> t' i)) $ mapM (id2CTyped t) ids
-         _ -> liftM (map(\i -> t' i <+> ie)) $ mapM (id2CTyped t) ids
+         (_, BTArray r _ _, [i], _) -> do
+            i' <- id2CTyped t i
+            ie' <- return $ case (r, mInitExpr, ignoreInit) of
+                (RangeInfinite, Nothing, False) -> text "= NULL" -- force dynamic array to be initialized as NULL if not initialized at all
+                (_, _, _) -> ie
+            result <- liftM (map(\i -> varDeclDecision isConst includeType (t' i) ie')) $ mapM (id2CTyped t) ids           
+            case (r, ignoreInit) of
+                (RangeInfinite, False) -> 
+                    -- if the array is dynamic, add dimension info to it
+                    return $ [dimDecl] ++ result
+                    where 
+                        arrayDimStr = show $ arrayDimension t
+                        arrayDimInitExp = text ("={" ++ ".dim = " ++ arrayDimStr ++ ", .a = {0, 0, 0, 0}}")
+                        dimDecl = varDeclDecision isConst includeType (text "fpcrtl_dimension_t" <+>  i' <> text "_dimension_info") arrayDimInitExp
+                    
+                (_, _) -> return result
+            
+         _ -> liftM (map(\i -> varDeclDecision isConst includeType (t' i) ie)) $ mapM (id2CTyped2 (Just $ t' empty) t) ids
     where
     initExpr Nothing = return $ empty
     initExpr (Just e) = liftM (text "=" <+>) (initExpr2C e)
+    varDeclDecision True True varStr expStr = varStr <+> expStr
+    varDeclDecision False True varStr expStr = if externVar then varStr else varStr <+> expStr
+    varDeclDecision False False varStr expStr = varStr <+> expStr
+    varDeclDecision True False varStr expStr = empty
+    arrayDimension a = case a of
+        ArrayDecl Nothing t -> let a = arrayDimension t in if a > 3 then error "Dynamic array with dimension > 4 is not supported." else 1 + arrayDimension t
+        ArrayDecl _ _ -> error "Mixed dynamic array and static array are not supported."
+        _ -> 0
 
-tvar2C f (OperatorDeclaration op (Identifier i _) ret params body) = do
+tvar2C f _ _ _ (OperatorDeclaration op (Identifier i _) inline ret params body) = do
     r <- op2CTyped op (extractTypes params)
-    fun2C f i (FunctionDeclaration r ret params body)
+    fun2C f i (FunctionDeclaration r inline ret params body)
 
 
 op2CTyped :: String -> [TypeDecl] -> State RenderState Identifier
@@ -489,6 +563,7 @@
                     "-" -> "sub"
                     "*" -> "mul"
                     "/" -> "div"
+                    "/(float)" -> "div"
                     "=" -> "eq"
                     "<" -> "lt"
                     ">" -> "gt"
@@ -591,7 +666,7 @@
              _ -> return $ \a -> i' <+> text "*" <+> a
     type2C' (PointerTo t) = liftM (\t a -> t (parens $ text "*" <> a)) $ type2C t
     type2C' (RecordType tvs union) = do
-        t <- withState' f $ mapM (tvar2C False) tvs
+        t <- withState' f $ mapM (tvar2C False False True False) tvs
         u <- unions
         return $ \i -> text "struct __" <> i <+> lbrace $+$ nest 4 ((vcat . map (<> semi) . concat $ t) $$ u) $+$ rbrace <+> i
         where
@@ -602,7 +677,7 @@
                          structs <- mapM struct2C a
                          return $ text "union" $+$ braces (nest 4 $ vcat structs) <> semi
             struct2C tvs = do
-                t <- withState' f $ mapM (tvar2C False) tvs
+                t <- withState' f $ mapM (tvar2C False False True False) tvs
                 return $ text "struct" $+$ braces (nest 4 (vcat . map (<> semi) . concat $ t)) <> semi
     type2C' (RangeType r) = return (text "int" <+>)
     type2C' (Sequence ids) = do
@@ -615,7 +690,7 @@
         t' <- type2C t
         lt <- gets lastType
         ft <- case lt of
-                BTFunction {} -> type2C (PointerTo t)
+                -- BTFunction {} -> type2C (PointerTo t)
                 _ -> return t'
         r' <- initExpr2C (InitRange r)
         return $ \i -> ft i <> brackets r'
@@ -675,15 +750,26 @@
                     e <- expr2C expr
                     return $ r <+> text "=" <+> e <> semi
                 _ -> error $ "Assignment to string from " ++ show lt
-        (BTArray _ _ _, _) -> phrase2C $
-            ProcCall (FunCall
-                [
-                Reference $ Address ref
-                , Reference $ Address $ RefExpression expr
-                , Reference $ FunCall [expr] (SimpleReference (Identifier "sizeof" BTUnknown))
-                ]
-                (SimpleReference (Identifier "memcpy" BTUnknown))
-                ) []
+        (BTArray _ _ _, _) -> do
+            case expr of
+                Reference er -> do
+                    exprRef <- ref2C er
+                    exprT <- gets lastType
+                    case exprT of
+                        BTArray RangeInfinite _ _ ->
+                            return $ text "FIXME: assign a dynamic array to an array"
+                        BTArray _ _ _ -> phrase2C $
+                                ProcCall (FunCall
+                                    [
+                                    Reference $ ref
+                                    , Reference $ RefExpression expr
+                                    , Reference $ FunCall [expr] (SimpleReference (Identifier "sizeof" BTUnknown))
+                                    ]
+                                    (SimpleReference (Identifier "memcpy" BTUnknown))
+                                    ) []
+                        _ -> return $ text "FIXME: assign a non-specific value to an array"
+
+                _ -> return $ text "FIXME: dynamic array assignment 2"
         _ -> do
             e <- expr2C expr
             return $ r <+> text "=" <+> e <> semi
@@ -704,7 +790,7 @@
         ph <- phrase2C p
         return $
              vcat (map (\i -> text "case" <+> i <> colon) . concat $ ies) <> nest 4 (ph $+$ text "break;")
-    dflt | isNothing mphrase = return []
+    dflt | isNothing mphrase = return [text "default: break;"] -- avoid compiler warning
          | otherwise = do
              ph <- mapM phrase2C $ fromJust mphrase
              return [text "default:" <+> nest 4 (vcat ph)]
@@ -713,18 +799,27 @@
     r <- ref2C ref
     t <- gets lastType
     case t of
-        (BTRecord _ rs) -> withRecordNamespace (render r ++ ".") rs $ phrase2C $ wrapPhrase p
+        (BTRecord _ rs) -> withRecordNamespace (render r ++ ".") (rec2Records rs) $ phrase2C $ wrapPhrase p
         a -> do
             error $ "'with' block referencing non-record type " ++ show a ++ "\n" ++ show wb
-phrase2C (ForCycle i' e1' e2' p) = do
+phrase2C (ForCycle i' e1' e2' p up) = do
     i <- id2C IOLookup i'
+    iType <- gets lastIdTypeDecl
     e1 <- expr2C e1'
     e2 <- expr2C e2'
-    ph <- phrase2C (wrapPhrase p)
-    return $
-        text "for" <> (parens . hsep . punctuate (char ';') $ [i <+> text "=" <+> e1, i <+> text "<=" <+> e2, text "++" <> i])
+    let inc = if up then "inc" else "dec"
+    let add = if up then "+ 1" else "- 1"
+    let iEnd = i <> text "__end__"
+    ph <- phrase2C . appendPhrase (BuiltInFunctionCall [Reference $ SimpleReference i'] (SimpleReference (Identifier inc BTUnknown))) $ wrapPhrase p
+    return . braces $
+        i <+> text "=" <+> e1 <> semi
         $$
-        ph
+        iType <+> iEnd <+> text "=" <+> e2 <> semi
+        $$ 
+        text "if" <+> (parens $ i <+> text "<=" <+> iEnd) <+> text "do" <+> ph <+>
+        text "while" <> parens (i <+> text "!=" <+> iEnd <+> text add) <> semi
+    where
+        appendPhrase p (Phrases ps) = Phrases $ ps ++ [p]
 phrase2C (RepeatCycle e' p') = do
     e <- expr2C e'
     p <- phrase2C (Phrases p')
@@ -777,12 +872,23 @@
             case expr2 of
                  SetExpression set -> do
                      ids <- mapM (id2C IOLookup) set
+                     modify(\s -> s{lastType = BTBool})
                      return . parens . hcat . punctuate (text " || ") . map (\i -> parens $ e1 <+> text "==" <+> i) $ ids
                  _ -> error "'in' against not set expression"
         (o, _, _) | o `elem` boolOps -> do
                         modify(\s -> s{lastType = BTBool})
                         return $ parens e1 <+> text o <+> parens e2
-                  | otherwise -> return $ parens e1 <+> text o <+> parens e2
+                  | otherwise -> do
+                        o' <- return $ case o of
+                            "/(float)" -> text "/(float)" -- pascal returns real value
+                            _ -> text o
+                        e1' <- return $ case (o, t1, t2) of
+                                ("-", BTInt, BTInt) -> parens $ text "(int64_t)" <+> parens e1
+                                _ -> parens e1
+                        e2' <- return $ case (o, t1, t2) of
+                                ("-", BTInt, BTInt) -> parens $ text "(int64_t)" <+> parens e2
+                                _ -> parens e2
+                        return $ e1' <+> o' <+> e2'
     where
         boolOps = ["==", "!=", "<", ">", "<=", ">="]
 expr2C (NumberLiteral s) = do
@@ -806,7 +912,12 @@
         BTRecord t _ -> do
             i <- op2CTyped op [SimpleType (Identifier t undefined)]
             ref2C $ FunCall [expr] (SimpleReference i)
-        _ -> return $ text (op2C op) <> e
+        BTBool -> do
+            o <- return $ case op of
+                     "not" -> text "!"
+                     _ -> text (op2C op)
+            return $ o <> parens e
+        _ -> return $ text (op2C op) <> parens e
 expr2C Null = return $ text "NULL"
 expr2C (CharCode a) = do
     modify(\s -> s{lastType = BTChar})
@@ -835,13 +946,13 @@
          _ -> error $ "BuiltInFunCall 'high' from " ++ show e ++ "\ntype: " ++ show lt
 expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "ord" _))) = liftM parens $ expr2C e
 expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "succ" _))) = liftM (<> text " + 1") $ expr2C e
-expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "pred" _))) = liftM (<> text " - 1") $ expr2C e
+expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "pred" _))) = liftM (<> text " - (int64_t)1") $ expr2C e
 expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "length" _))) = do
     e' <- expr2C e
     lt <- gets lastType
     modify (\s -> s{lastType = BTInt})
     case lt of
-         BTString -> return $ text "Length" <> parens e'
+         BTString -> return $ text "fpcrtl_Length" <> parens e'
          BTArray RangeInfinite _ _ -> error $ "length() called on variable size array " ++ show e'
          BTArray (RangeFromTo _ n) _ _ -> initExpr2C (BuiltInFunction "succ" [n])
          _ -> error $ "length() called on " ++ show lt
@@ -864,7 +975,7 @@
     case t of
          BTFunction _ _ rt -> do
              modify(\s -> s{lastType = rt})
-             return $ i <> parens empty
+             return $ i <> parens empty --xymeng: removed parens
          _ -> return $ i
 ref2CF r@(RecordField (SimpleReference _) (SimpleReference _)) = do
     i <- ref2C r
@@ -907,7 +1018,7 @@
     r1 <- ref2C ref1
     t <- fromPointer (show ref1) =<< gets lastType
     r2 <- case t of
-        BTRecord _ rs -> withRecordNamespace "" rs $ ref2C ref2
+        BTRecord _ rs -> withRecordNamespace "" (rec2Records rs) $ ref2C ref2
         BTUnit -> error "What??"
         a -> error $ "dereferencing from " ++ show a ++ "\n" ++ show rf
     return $
@@ -917,7 +1028,7 @@
     t <- gets lastType
     case t of
         BTRecord _ rs -> do
-            r2 <- withRecordNamespace "" rs $ ref2C ref2
+            r2 <- withRecordNamespace "" (rec2Records rs) $ ref2C ref2
             return $ r1 <> text "." <> r2
         BTUnit -> withLastIdNamespace $ ref2C ref2
         a -> error $ "dereferencing from " ++ show a ++ "\n" ++ show rf
@@ -962,7 +1073,7 @@
 op2C :: String -> String
 op2C "or" = "|"
 op2C "and" = "&"
-op2C "not" = "!"
+op2C "not" = "~"
 op2C "xor" = "^"
 op2C "div" = "/"
 op2C "mod" = "%"
@@ -970,5 +1081,6 @@
 op2C "shr" = ">>"
 op2C "<>" = "!="
 op2C "=" = "=="
+op2C "/" = "/(float)"
 op2C a = a