merge spacecampaign
authorkoda
Mon, 28 Oct 2013 14:07:04 +0100
changeset 9648 3a3defce1b28
parent 9583 37a6d807c140 (diff)
parent 9646 7588daa8d28f (current diff)
child 9649 2d30721b1a11
merge spacecampaign
QTfrontend/hedgewars.qrc
QTfrontend/hwform.cpp
QTfrontend/hwform.h
hedgewars/uScript.pas
--- a/QTfrontend/CMakeLists.txt	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/CMakeLists.txt	Mon Oct 28 14:07:04 2013 +0100
@@ -13,6 +13,12 @@
 find_package(Qt4 REQUIRED)
 include(${QT_USE_FILE})
 
+if(APPLE AND
+   ${QTVERSION} VERSION_GREATER "4.7.0" AND
+   ${QTVERSION} VERSION_LESS "4.7.4")
+    message(FATAL_ERROR "This version of QT is known *not* to work, please update or use a lower version")
+endif()
+
 find_package(SDL REQUIRED)       #video in SDLInteraction
 find_package(SDL_mixer REQUIRED) #audio in SDLInteraction
 
--- a/QTfrontend/drawmapscene.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/drawmapscene.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -20,6 +20,8 @@
 #include <QGraphicsPathItem>
 #include <QtEndian>
 #include <QDebug>
+#include <QTransform>
+#include <math.h>
 
 #include "drawmapscene.h"
 
@@ -44,6 +46,8 @@
     setBackgroundBrush(m_eraser);
     m_isErasing = false;
 
+    m_pathType = Polyline;
+
     m_pen.setWidth(76);
     m_pen.setJoinStyle(Qt::RoundJoin);
     m_pen.setCapStyle(Qt::RoundCap);
@@ -60,19 +64,45 @@
     if(m_currPath && (mouseEvent->buttons() & Qt::LeftButton))
     {
         QPainterPath path = m_currPath->path();
+        QPointF currentPos = mouseEvent->scenePos();
 
         if(mouseEvent->modifiers() & Qt::ControlModifier)
+            currentPos = putSomeConstraints(paths.first().initialPoint, currentPos);
+
+        switch (m_pathType)
         {
-            int c = path.elementCount();
-            QPointF pos = mouseEvent->scenePos();
-            path.setElementPositionAt(c - 1, pos.x(), pos.y());
+        case Polyline:
+            if(mouseEvent->modifiers() & Qt::ControlModifier)
+            {
+                int c = path.elementCount();
+                path.setElementPositionAt(c - 1, currentPos.x(), currentPos.y());
 
+            }
+            else
+            {
+                path.lineTo(currentPos);
+                paths.first().points.append(mouseEvent->scenePos().toPoint());
+            }
+            break;
+        case Rectangle: {
+            path = QPainterPath();
+            QPointF p1 = paths.first().initialPoint;
+            QPointF p2 = currentPos;
+            path.moveTo(p1);
+            path.lineTo(p1.x(), p2.y());
+            path.lineTo(p2);
+            path.lineTo(p2.x(), p1.y());
+            path.lineTo(p1);
+            break;
+            }
+        case Ellipse: {
+            path = QPainterPath();
+            QList<QPointF> points = makeEllipse(paths.first().initialPoint, currentPos);
+            path.addPolygon(QPolygonF(QVector<QPointF>::fromList(points)));
+            break;
         }
-        else
-        {
-            path.lineTo(mouseEvent->scenePos());
-            paths.first().points.append(mouseEvent->scenePos().toPoint());
         }
+
         m_currPath->setPath(path);
 
         emit pathChanged();
@@ -96,7 +126,8 @@
     PathParams params;
     params.width = serializePenWidth(m_pen.width());
     params.erasing = m_isErasing;
-    params.points = QList<QPoint>() << mouseEvent->scenePos().toPoint();
+    params.initialPoint = mouseEvent->scenePos().toPoint();
+    params.points = QList<QPoint>() << params.initialPoint;
     paths.prepend(params);
     m_currPath->setPath(path);
 
@@ -107,14 +138,43 @@
 {
     if (m_currPath)
     {
-        QPainterPath path = m_currPath->path();
-        path.lineTo(mouseEvent->scenePos());
-        paths.first().points.append(mouseEvent->scenePos().toPoint());
-        m_currPath->setPath(path);
+        QPointF currentPos = mouseEvent->scenePos();
+
+        if(mouseEvent->modifiers() & Qt::ControlModifier)
+            currentPos = putSomeConstraints(paths.first().initialPoint, currentPos);
 
-        simplifyLast();
+        switch (m_pathType)
+        {
+        case Polyline: {
+            QPainterPath path = m_currPath->path();
+            path.lineTo(mouseEvent->scenePos());
+            paths.first().points.append(currentPos.toPoint());
+            m_currPath->setPath(path);
+            simplifyLast();
+            break;
+        }
+        case Rectangle: {
+            QPoint p1 = paths.first().initialPoint;
+            QPoint p2 = currentPos.toPoint();
+            QList<QPoint> rpoints;
+            rpoints << p1 << QPoint(p1.x(), p2.y()) << p2 << QPoint(p2.x(), p1.y()) << p1;
+            paths.first().points = rpoints;
+            break;
+        }
+        case Ellipse:
+            QPoint p1 = paths.first().initialPoint;
+            QPoint p2 = currentPos.toPoint();
+            QList<QPointF> points = makeEllipse(p1, p2);
+            QList<QPoint> epoints;
+            foreach(const QPointF & p, points)
+                epoints.append(p.toPoint());
+            paths.first().points = epoints;
+            break;
+        }
 
         m_currPath = 0;
+
+        emit pathChanged();
     }
 }
 
@@ -156,7 +216,7 @@
     if(m_isCursorShown)
         return;
 
-    if(items().size())
+    if(paths.size())
     {
         removeItem(items().first());
         paths.removeFirst();
@@ -183,15 +243,18 @@
     if(!items().size())
         return;
 
+    m_specialPoints.clear();
     oldItems.clear();
 
     // do this since clear() would _destroy_ all items
-    while(items().size())
+    for(int i = paths.size() - 1; i >= 0; --i)
     {
         oldItems.push_front(items().first());
         removeItem(items().first());
     }
 
+    items().clear();
+
     oldPaths = paths;
 
     paths.clear();
@@ -211,7 +274,7 @@
 
 QByteArray DrawMapScene::encode()
 {
-    QByteArray b;
+    QByteArray b(m_specialPoints);
 
     for(int i = paths.size() - 1; i >= 0; --i)
     {
@@ -247,9 +310,12 @@
     oldPaths.clear();
     clear();
     paths.clear();
+    m_specialPoints.clear();
 
     PathParams params;
 
+    bool isSpecial = true;
+
     while(data.size() >= 5)
     {
         qint16 px = qFromBigEndian(*(qint16 *)data.data());
@@ -258,9 +324,11 @@
         data.remove(0, 2);
         quint8 flags = *(quint8 *)data.data();
         data.remove(0, 1);
-
+        qDebug() << px << py;
         if(flags & 0x80)
         {
+            isSpecial = false;
+
             if(params.points.size())
             {
                 addPath(pointsToPath(params.points), m_pen);
@@ -278,9 +346,23 @@
             else
                 m_pen.setBrush(m_brush);
             params.width = penWidth;
-        }
+        } else
+            if(isSpecial)
+            {
+                QPainterPath path;
+                path.addEllipse(QPointF(px, py), 10, 10);
+
+                addPath(path);
 
-        params.points.append(QPoint(px, py));
+                qint16 x = qToBigEndian(px);
+                qint16 y = qToBigEndian(py);
+                m_specialPoints.append((const char *)&x, 2);
+                m_specialPoints.append((const char *)&y, 2);
+                m_specialPoints.append((const char *)&flags, 1);
+            }
+
+        if(!isSpecial)
+            params.points.append(QPoint(px, py));
     }
 
     if(params.points.size())
@@ -323,8 +405,6 @@
         QGraphicsPathItem * pathItem = static_cast<QGraphicsPathItem *>(items()[m_isCursorShown ? 1 : 0]);
         pathItem->setPath(pointsToPath(paths[0].points));
     }
-
-    emit pathChanged();
 }
 
 int DrawMapScene::pointsCount()
@@ -361,3 +441,47 @@
 {
     return width * 10 + 6;
 }
+
+void DrawMapScene::setPathType(PathType pathType)
+{
+    m_pathType = pathType;
+}
+
+QList<QPointF> DrawMapScene::makeEllipse(const QPointF &center, const QPointF &corner)
+{
+    QList<QPointF> l;
+    qreal rx = qAbs(center.x() - corner.x());
+    qreal ry = qAbs(center.y() - corner.y());
+    qreal r = qMax(rx, ry);
+
+    if(r < 4)
+    {
+        l.append(center);
+    } else
+    {
+        qreal angleDelta = qMax(0.1, qMin(0.7, 120 / r));
+        for(qreal angle = 0.0; angle < 2*M_PI; angle += angleDelta)
+            l.append(center + QPointF(rx * cos(angle), ry * sin(angle)));
+        l.append(l.first());
+    }
+
+    return l;
+}
+
+QPointF DrawMapScene::putSomeConstraints(const QPointF &initialPoint, const QPointF &point)
+{
+    QPointF vector = point - initialPoint;
+
+    for(int angle = 0; angle < 180; angle += 15)
+    {
+        QTransform transform;
+        transform.rotate(angle);
+
+        QPointF rotated = transform.map(vector);
+
+        if(rotated.x() == 0) return point;
+        if(qAbs(rotated.y() / rotated.x()) < 0.05) return initialPoint + transform.inverted().map(QPointF(rotated.x(), 0));
+    }
+
+    return point;
+}
--- a/QTfrontend/drawmapscene.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/drawmapscene.h	Mon Oct 28 14:07:04 2013 +0100
@@ -29,6 +29,7 @@
 {
     quint8 width;
     bool erasing;
+    QPoint initialPoint;
     QList<QPoint> points;
 };
 
@@ -38,6 +39,12 @@
 {
         Q_OBJECT
     public:
+        enum PathType {
+            Polyline  = 0,
+            Rectangle = 1,
+            Ellipse   = 2
+        };
+
         explicit DrawMapScene(QObject *parent = 0);
 
         QByteArray encode();
@@ -54,6 +61,7 @@
         void setErasing(bool erasing);
         void showCursor();
         void hideCursor();
+        void setPathType(PathType pathType);
 
     private:
         QPen m_pen;
@@ -66,6 +74,8 @@
         QList<QGraphicsItem *> oldItems;
         QGraphicsEllipseItem * m_cursor;
         bool m_isCursorShown;
+        QByteArray m_specialPoints;
+        PathType m_pathType;
 
         virtual void mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent);
         virtual void mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent);
@@ -76,6 +86,8 @@
 
         quint8 serializePenWidth(int width);
         int deserializePenWidth(quint8 width);
+        QList<QPointF> makeEllipse(const QPointF & center, const QPointF & corner);
+        QPointF putSomeConstraints(const QPointF & initialPoint, const QPointF & point);
 };
 
 #endif // DRAWMAPSCENE_H
--- a/QTfrontend/game.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/game.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -89,18 +89,6 @@
     SetGameState(gsStopped);
 }
 
-void HWGame::addKeyBindings(QByteArray * buf)
-{
-    for(int i = 0; i < BINDS_NUMBER; i++)
-    {
-        QString value = config->value(QString("Binds/%1").arg(cbinds[i].action), cbinds[i].strbind).toString();
-        if (value.isEmpty() || value == "default") continue;
-
-        QString bind = QString("edbind " + value + " " + cbinds[i].action);
-        HWProto::addStringToBuffer(*buf, bind);
-    }
-}
-
 void HWGame::commonConfig()
 {
     QByteArray buf;
@@ -118,8 +106,6 @@
     }
     HWProto::addStringToBuffer(buf, gt);
 
-    addKeyBindings(&buf);
-
     buf += gamecfg->getFullConfig();
 
     if (m_pTeamSelWidget)
@@ -132,7 +118,7 @@
             HWProto::addStringToBuffer(buf, QString("eammreinf %1").arg(ammostr.mid(3 * cAmmoNumber, cAmmoNumber)));
             if(gamecfg->schemeData(15).toBool() || !gamecfg->schemeData(21).toBool()) HWProto::addStringToBuffer(buf, QString("eammstore"));
             HWProto::addStringListToBuffer(buf,
-                                           team.teamGameConfig(gamecfg->getInitHealth(), config));
+                                           team.teamGameConfig(gamecfg->getInitHealth()));
             ;
         }
     }
@@ -150,8 +136,6 @@
     QByteArray teamscfg;
     ThemeModel * themeModel = DataManager::instance().themeModel();
 
-    addKeyBindings(&teamscfg);
-
     HWProto::addStringToBuffer(teamscfg, "TL");
     HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
                                .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount()).data(ThemeModel::ActualNameRole).toString() : "steel"));
@@ -165,7 +149,7 @@
     team1.setNumHedgehogs(4);
     HWNamegen::teamRandomNames(team1,true);
     HWProto::addStringListToBuffer(teamscfg,
-                                   team1.teamGameConfig(100, config));
+                                   team1.teamGameConfig(100));
 
     HWTeam team2;
     team2.setDifficulty(4);
@@ -175,7 +159,7 @@
         HWNamegen::teamRandomNames(team2,true);
     while(!team2.name().compare(team1.name()) || !team2.hedgehog(0).Hat.compare(team1.hedgehog(0).Hat));
     HWProto::addStringListToBuffer(teamscfg,
-                                   team2.teamGameConfig(100, config));
+                                   team2.teamGameConfig(100));
 
     HWProto::addStringToBuffer(teamscfg, QString("eammloadt %1").arg(cDefaultAmmoStore->mid(0, cAmmoNumber)));
     HWProto::addStringToBuffer(teamscfg, QString("eammprob %1").arg(cDefaultAmmoStore->mid(cAmmoNumber, cAmmoNumber)));
@@ -194,8 +178,6 @@
     HWProto::addStringToBuffer(traincfg, "eseed " + QUuid::createUuid().toString());
     HWProto::addStringToBuffer(traincfg, "escript " + training);
 
-    addKeyBindings(&traincfg);
-
     RawSendIPC(traincfg);
 }
 
@@ -207,8 +189,6 @@
 
     HWProto::addStringToBuffer(campaigncfg, "escript " + campaignScript);
 
-    addKeyBindings(&campaigncfg);
-
     RawSendIPC(campaigncfg);
 }
 
--- a/QTfrontend/game.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/game.h	Mon Oct 28 14:07:04 2013 +0100
@@ -113,7 +113,6 @@
         GameType gameType;
         QByteArray m_netSendBuffer;
 
-        void addKeyBindings(QByteArray * buf);
         void commonConfig();
         void SendConfig();
         void SendQuickConfig();
--- a/QTfrontend/gameuiconfig.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/gameuiconfig.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -159,6 +159,8 @@
     Form->ui.pageOptions->leProxyLogin->setText(value("proxy/login", "").toString());
     Form->ui.pageOptions->leProxyPassword->setText(value("proxy/password", "").toString());
 
+    applyProxySettings();
+
     { // load colors
         QStandardItemModel * model = DataManager::instance().colorsModel();
         for(int i = model->rowCount() - 1; i >= 0; --i)
@@ -310,22 +312,7 @@
             setValue("proxy/password", Form->ui.pageOptions->leProxyPassword->text());
         }
 
-        QNetworkProxy proxy;
-
-        if(proxyType == PageOptions::SystemProxy)
-        {
-            // use system proxy settings
-            proxy = QNetworkProxyFactory::systemProxyForQuery().at(0);
-        } else
-        {
-            proxy.setType(proxyTypesMap[proxyType]);
-            proxy.setHostName(Form->ui.pageOptions->leProxy->text());
-            proxy.setPort(Form->ui.pageOptions->sbProxyPort->value());
-            proxy.setUser(Form->ui.pageOptions->leProxyLogin->text());
-            proxy.setPassword(Form->ui.pageOptions->leProxyPassword->text());
-        }
-
-        QNetworkProxy::setApplicationProxy(proxy);
+        applyProxySettings();
     }
 
     { // save colors
@@ -665,3 +652,25 @@
     m_binds[bindID].strbind = strbind;
     setValue(QString("Binds/%1").arg(m_binds[bindID].action), strbind);
 }
+
+void GameUIConfig::applyProxySettings()
+{
+    QNetworkProxy proxy;
+
+    int proxyType = Form->ui.pageOptions->cbProxyType->currentIndex();
+
+    if(proxyType == PageOptions::SystemProxy)
+    {
+        // use system proxy settings
+        proxy = QNetworkProxyFactory::systemProxyForQuery().at(0);
+    } else
+    {
+        proxy.setType(proxyTypesMap[proxyType]);
+        proxy.setHostName(Form->ui.pageOptions->leProxy->text());
+        proxy.setPort(Form->ui.pageOptions->sbProxyPort->value());
+        proxy.setUser(Form->ui.pageOptions->leProxyLogin->text());
+        proxy.setPassword(Form->ui.pageOptions->leProxyPassword->text());
+    }
+
+    QNetworkProxy::setApplicationProxy(proxy);
+}
--- a/QTfrontend/gameuiconfig.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/gameuiconfig.h	Mon Oct 28 14:07:04 2013 +0100
@@ -99,6 +99,8 @@
         bool eventFilter(QObject *object, QEvent *event);
         QString temphash;
         QList<BindAction> m_binds;
+
+        void applyProxySettings();
 };
 
 #endif
--- a/QTfrontend/hedgewars.qrc	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/hedgewars.qrc	Mon Oct 28 14:07:04 2013 +0100
@@ -128,6 +128,7 @@
         <file>res/iconMine.png</file>
         <file>res/iconDud.png</file>
         <file>res/iconRope.png</file>
+        <file>res/iconEarth.png</file>
         <file>res/dice.png</file>
         <file>res/Star.png</file>
         <file>res/inverse-corner-bl.png</file>
@@ -179,5 +180,9 @@
         <file>res/splash.png</file>
         <file>res/html/about.html</file>
         <file>res/xml/tips.xml</file>
+        <file>res/chat/hedgehogcontributor.png</file>
+        <file>res/chat/hedgehogcontributor_gray.png</file>
+        <file>res/chat/roomadmincontributor.png</file>
+        <file>res/chat/roomadmincontributor_gray.png</file>
     </qresource>
 </RCC>
--- a/QTfrontend/hwform.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/hwform.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -1130,7 +1130,7 @@
         //ForcedDisconnect(tr("No nickname supplied."));
         bool retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
         GoBack();
-        if (retry) {
+        if (retry && hwnet) {
             if (hwnet->m_private_game) {
                 QStringList list = hwnet->getHost().split(":");
                 NetConnectServer(list.at(0), list.at(1).toShort());
@@ -1140,7 +1140,8 @@
         return;
     }
 
-    hwnet->NewNick(newNick);
+    if(hwnet)
+        hwnet->NewNick(newNick);
     config->setValue("net/nick", newNick);
     config->updNetNick();
 
@@ -1164,6 +1165,13 @@
     }
 }
 
+void HWForm::askRoomPassword()
+{
+    QString password = QInputDialog::getText(this, tr("Room password"), tr("The room is protected with password.\nPlease, enter the password:"));
+    if(hwnet && !password.isEmpty())
+        hwnet->roomPasswordEntered(password);
+}
+
 bool HWForm::RetryDialog(const QString & title, const QString & label)
 {
     QMessageBox retryMsg(this);
@@ -1243,6 +1251,7 @@
     connect(hwnet, SIGNAL(NickTaken(const QString&)), this, SLOT(NetNickTaken(const QString&)), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(AuthFailed()), this, SLOT(NetAuthFailed()), Qt::QueuedConnection);
     //connect(ui.pageNetGame->BtnBack, SIGNAL(clicked()), hwnet, SLOT(partRoom()));
+    connect(hwnet, SIGNAL(askForRoomPassword()), this, SLOT(askRoomPassword()), Qt::QueuedConnection);
 
     ui.pageRoomsList->chatWidget->setUsersModel(hwnet->lobbyPlayersModel());
     ui.pageNetGame->chatWidget->setUsersModel(hwnet->roomPlayersModel());
@@ -1257,10 +1266,10 @@
     connect(hwnet, SIGNAL(serverMessage(const QString&)),
             ui.pageRoomsList->chatWidget, SLOT(onServerMessage(const QString&)), Qt::QueuedConnection);
 
-    connect(ui.pageRoomsList, SIGNAL(askForCreateRoom(const QString &)),
-            hwnet, SLOT(CreateRoom(const QString&)));
-    connect(ui.pageRoomsList, SIGNAL(askForJoinRoom(const QString &)),
-            hwnet, SLOT(JoinRoom(const QString&)));
+    connect(ui.pageRoomsList, SIGNAL(askForCreateRoom(const QString &, const QString &)),
+            hwnet, SLOT(CreateRoom(const QString&, const QString &)));
+    connect(ui.pageRoomsList, SIGNAL(askForJoinRoom(const QString &, const QString &)),
+            hwnet, SLOT(JoinRoom(const QString&, const QString &)));
 //  connect(ui.pageRoomsList, SIGNAL(askForCreateRoom(const QString &)),
 //      this, SLOT(NetGameMaster()));
 //  connect(ui.pageRoomsList, SIGNAL(askForJoinRoom(const QString &)),
--- a/QTfrontend/hwform.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/hwform.h	Mon Oct 28 14:07:04 2013 +0100
@@ -112,6 +112,7 @@
         void NetNickNotRegistered(const QString & nick);
         void NetNickTaken(const QString & nick);
         void NetAuthFailed();
+        void askRoomPassword();
         bool RetryDialog(const QString & title, const QString & label);
         void NetTeamAccepted(const QString& team);
         void AddNetTeam(const HWTeam& team);
--- a/QTfrontend/model/ammoSchemeModel.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/model/ammoSchemeModel.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -64,6 +64,7 @@
                                 << QVariant(5)             // health dec amt 38
                                 << QVariant(100)           // rope modfier   39
                                 << QVariant(100)           // get away time  40
+                                << QVariant(0)             // world edge     41
                                 ;
 
 AmmoSchemeModel::AmmoSchemeModel(QObject* parent, const QString & fileName) :
@@ -128,6 +129,7 @@
               << "healthdecrease"      // 38
               << "ropepct"             // 39
               << "getawaytime"         // 40
+              << "worldedge"           // 41
               ;
 
     QList<QVariant> proMode;
@@ -173,6 +175,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> shoppa;
@@ -218,6 +221,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> cleanslate;
@@ -263,6 +267,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> minefield;
@@ -308,6 +313,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> barrelmayhem;
@@ -353,6 +359,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> tunnelhogs;
@@ -398,6 +405,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> forts;
@@ -443,6 +451,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> timeless;
@@ -488,6 +497,7 @@
             << QVariant(0)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> thinkingportals;
@@ -533,6 +543,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
     QList<QVariant> kingmode;
@@ -578,6 +589,7 @@
             << QVariant(5)             // health dec amt 38
             << QVariant(100)           // rope modfier   39
             << QVariant(100)           // get away time  40
+            << QVariant(0)             // world edge     41
             ;
 
 
--- a/QTfrontend/model/playerslistmodel.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/model/playerslistmodel.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -11,7 +11,8 @@
 PlayersListModel::PlayersListModel(QObject *parent) :
     QAbstractListModel(parent)
 {
-
+    m_fontInRoom = QFont();
+    m_fontInRoom.setItalic(true);
 }
 
 
@@ -223,6 +224,7 @@
         << index.data(Ignore).toBool()
         << index.data(InGame).toBool()
         << index.data(RoomFilterRole).toBool()
+        << index.data(InRoom).toBool()
         ;
 
     for(int i = flags.size() - 1; i >= 0; --i)
@@ -253,16 +255,26 @@
                 else
                     painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/chat/lamp_off.png"));
             }
+        } else
+        { // we're in lobby
+            if(!index.data(InRoom).toBool())
+                painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/Flake.png"));
         }
 
         QString mainIconName(":/res/chat/");
 
-        if(index.data(RoomAdmin).toBool())
-            mainIconName += "roomadmin";
-        else if(index.data(ServerAdmin).toBool())
+        if(index.data(ServerAdmin).toBool())
             mainIconName += "serveradmin";
         else
-            mainIconName += "hedgehog";
+        {
+            if(index.data(RoomAdmin).toBool())
+                mainIconName += "roomadmin";
+            else
+                mainIconName += "hedgehog";
+
+            if(index.data(Contributor).toBool())
+                mainIconName += "contributor";
+        }
 
         if(!index.data(Registered).toBool())
             mainIconName += "_gray";
--- a/QTfrontend/model/playerslistmodel.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/model/playerslistmodel.h	Mon Oct 28 14:07:04 2013 +0100
@@ -6,6 +6,7 @@
 #include <QIcon>
 #include <QModelIndex>
 #include <QSet>
+#include <QFont>
 
 class PlayersListModel : public QAbstractListModel
 {
@@ -19,7 +20,9 @@
         Registered  = Qt::UserRole + 3,
         Friend      = Qt::UserRole + 4,
         Ignore      = Qt::UserRole + 5,
-        InGame      = Qt::UserRole + 6
+        InGame      = Qt::UserRole + 6,
+        InRoom      = Qt::UserRole + 7,
+        Contributor = Qt::UserRole + 8
     };
 
     enum SpecialRoles {
@@ -61,6 +64,7 @@
     QList<DataEntry> m_data;
     QSet<QString> m_friendsSet, m_ignoredSet;
     QString m_nickname;
+    QFont m_fontInRoom;
 
     void updateIcon(const QModelIndex & index);
     void updateSortData(const QModelIndex & index);
--- a/QTfrontend/net/newnetclient.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/net/newnetclient.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -94,7 +94,7 @@
     NetSocket.disconnectFromHost();
 }
 
-void HWNewNet::CreateRoom(const QString & room)
+void HWNewNet::CreateRoom(const QString & room, const QString & password)
 {
     if(netClientState != InLobby)
     {
@@ -104,11 +104,15 @@
 
     myroom = room;
 
-    RawSendNet(QString("CREATE_ROOM%1%2").arg(delimeter).arg(room));
+    if(password.isEmpty())
+        RawSendNet(QString("CREATE_ROOM%1%2").arg(delimeter).arg(room));
+    else
+        RawSendNet(QString("CREATE_ROOM%1%2%1%3").arg(delimeter).arg(room).arg(password));
+
     isChief = true;
 }
 
-void HWNewNet::JoinRoom(const QString & room)
+void HWNewNet::JoinRoom(const QString & room, const QString &password)
 {
     if(netClientState != InLobby)
     {
@@ -118,7 +122,11 @@
 
     myroom = room;
 
-    RawSendNet(QString("JOIN_ROOM%1%2").arg(delimeter).arg(room));
+    if(password.isEmpty())
+        RawSendNet(QString("JOIN_ROOM%1%2").arg(delimeter).arg(room));
+    else
+        RawSendNet(QString("JOIN_ROOM%1%2%1%3").arg(delimeter).arg(room).arg(password));
+
     isChief = false;
 }
 
@@ -436,6 +444,16 @@
                         foreach(const QString & nick, nicks)
                             m_playersModel->setFlag(nick, PlayersListModel::Registered, setFlag);
                         break;
+                // flag indicating if a player is in room
+                case 'i':
+                        foreach(const QString & nick, nicks)
+                            m_playersModel->setFlag(nick, PlayersListModel::InRoom, setFlag);
+                        break;
+                // flag indicating if a player is contributor
+                case 'c':
+                        foreach(const QString & nick, nicks)
+                            m_playersModel->setFlag(nick, PlayersListModel::InRoom, setFlag);
+                        break;
                 // flag indicating if a player has engine running
                 case 'g':
                     if(inRoom)
@@ -1055,10 +1073,11 @@
     switch(n)
     {
         case 0:
-        {
             emit NickTaken(mynick);
             break;
-        }
+        case 2:
+            emit askForRoomPassword();
+            break;
     }
 }
 
@@ -1076,3 +1095,9 @@
 {
     return m_roomPlayersModel;
 }
+
+void HWNewNet::roomPasswordEntered(const QString &password)
+{
+    if(!myroom.isEmpty())
+        JoinRoom(myroom, password);
+}
--- a/QTfrontend/net/newnetclient.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/net/newnetclient.h	Mon Oct 28 14:07:04 2013 +0100
@@ -76,6 +76,7 @@
         PlayersListModel * m_playersModel;
         QSortFilterProxyModel * m_lobbyPlayersModel;
         QSortFilterProxyModel * m_roomPlayersModel;
+        QString m_lastRoom;
 
         QStringList cmdbuf;
 
@@ -103,6 +104,7 @@
         void adminAccess(bool);
         void roomMaster(bool);
         void roomNameUpdated(const QString & name);
+        void askForRoomPassword();
 
         void netSchemeConfig(QStringList &);
         void paramChanged(const QString & param, const QStringList & value);
@@ -153,8 +155,8 @@
         void setLatestProtocolVar(int proto);
         void askServerVars();
 
-        void JoinRoom(const QString & room);
-        void CreateRoom(const QString & room);
+        void JoinRoom(const QString & room, const QString & password);
+        void CreateRoom(const QString & room, const QString &password);
         void updateRoomName(const QString &);
         void askRoomsList();
         void gameFinished(bool correcly);
@@ -173,6 +175,7 @@
         void removeBan(const QString &);
         void banIP(const QString & ip, const QString & reason, int seconds);
         void banNick(const QString & nick, const QString & reason, int seconds);
+        void roomPasswordEntered(const QString & password);
 
     private slots:
         void ClientRead();
Binary file QTfrontend/res/chat/hedgehogcontributor.png has changed
Binary file QTfrontend/res/chat/hedgehogcontributor_gray.png has changed
Binary file QTfrontend/res/chat/roomadmincontributor.png has changed
Binary file QTfrontend/res/chat/roomadmincontributor_gray.png has changed
Binary file QTfrontend/res/iconEarth.png has changed
--- a/QTfrontend/team.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/team.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -261,7 +261,7 @@
     return true;
 }
 
-QStringList HWTeam::teamGameConfig(quint32 InitHealth, GameUIConfig * config) const
+QStringList HWTeam::teamGameConfig(quint32 InitHealth) const
 {
     QStringList sl;
     if (m_isNetTeam)
--- a/QTfrontend/team.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/team.h	Mon Oct 28 14:07:04 2013 +0100
@@ -93,7 +93,7 @@
         void incWins();
 
         // convert team info into strings for further computation
-        QStringList teamGameConfig(quint32 InitHealth, GameUIConfig * config) const;
+        QStringList teamGameConfig(quint32 InitHealth) const;
 
         // comparison operators
         bool operator == (const HWTeam& t1) const;
--- a/QTfrontend/ui/mouseoverfilter.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/mouseoverfilter.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -49,8 +49,6 @@
         {
             SDLInteraction::instance().playSoundFile("/Sounds/steps.ogg");
         }
-
-        return true;
     }
     else if (event->type() == QEvent::Leave)
     {
@@ -63,7 +61,6 @@
         else
             abstractpage->setButtonDescription("");
     }
-
     return false;
 }
 
--- a/QTfrontend/ui/page/pagedrawmap.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pagedrawmap.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -20,6 +20,7 @@
 #include <QPushButton>
 #include <QFileDialog>
 #include <QCheckBox>
+#include <QRadioButton>
 
 #include "pagedrawmap.h"
 #include "drawmapwidget.h"
@@ -32,12 +33,22 @@
     cbEraser = new QCheckBox(tr("Eraser"), this);
     pageLayout->addWidget(cbEraser, 0, 0);
     pbUndo = addButton(tr("Undo"), pageLayout, 1, 0);
-    pbClear = addButton(tr("Clear"), pageLayout, 2, 0);
-    pbLoad = addButton(tr("Load"), pageLayout, 3, 0);
-    pbSave = addButton(tr("Save"), pageLayout, 4, 0);
+
+    rbPolyline = new QRadioButton(tr("Polyline"), this);
+    pageLayout->addWidget(rbPolyline, 2, 0);
+    rbRectangle = new QRadioButton(tr("Rectangle"), this);
+    pageLayout->addWidget(rbRectangle, 3, 0);
+    rbEllipse = new QRadioButton(tr("Ellipse"), this);
+    pageLayout->addWidget(rbEllipse, 4, 0);
+
+    rbPolyline->setChecked(true);
+
+    pbClear = addButton(tr("Clear"), pageLayout, 5, 0);
+    pbLoad = addButton(tr("Load"), pageLayout, 6, 0);
+    pbSave = addButton(tr("Save"), pageLayout, 7, 0);
 
     drawMapWidget = new DrawMapWidget(this);
-    pageLayout->addWidget(drawMapWidget, 0, 1, 6, 1);
+    pageLayout->addWidget(drawMapWidget, 0, 1, 9, 1);
 
     return pageLayout;
 }
@@ -49,6 +60,10 @@
     connect(pbClear, SIGNAL(clicked()), drawMapWidget, SLOT(clear()));
     connect(pbLoad, SIGNAL(clicked()), this, SLOT(load()));
     connect(pbSave, SIGNAL(clicked()), this, SLOT(save()));
+
+    connect(rbPolyline, SIGNAL(toggled(bool)), this, SLOT(pathTypeSwitched(bool)));
+    connect(rbRectangle, SIGNAL(toggled(bool)), this, SLOT(pathTypeSwitched(bool)));
+    connect(rbEllipse, SIGNAL(toggled(bool)), this, SLOT(pathTypeSwitched(bool)));
 }
 
 PageDrawMap::PageDrawMap(QWidget* parent) : AbstractPage(parent)
@@ -71,3 +86,13 @@
     if(!fileName.isEmpty())
         drawMapWidget->save(fileName);
 }
+
+void PageDrawMap::pathTypeSwitched(bool b)
+{
+    if(b)
+    {
+        if(rbPolyline->isChecked()) drawMapWidget->setPathType(DrawMapScene::Polyline);
+        else if(rbRectangle->isChecked()) drawMapWidget->setPathType(DrawMapScene::Rectangle);
+        else if(rbEllipse->isChecked()) drawMapWidget->setPathType(DrawMapScene::Ellipse);
+    }
+}
--- a/QTfrontend/ui/page/pagedrawmap.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pagedrawmap.h	Mon Oct 28 14:07:04 2013 +0100
@@ -22,6 +22,7 @@
 #include "AbstractPage.h"
 
 class DrawMapWidget;
+class QRadioButton;
 
 class PageDrawMap : public AbstractPage
 {
@@ -42,10 +43,14 @@
         QPushButton * pbLoad;
         QPushButton * pbSave;
         QCheckBox * cbEraser;
+        QRadioButton * rbPolyline;
+        QRadioButton * rbRectangle;
+        QRadioButton * rbEllipse;
 
     private slots:
         void load();
         void save();
+        void pathTypeSwitched(bool b);
 };
 
 #endif
--- a/QTfrontend/ui/page/pageeditteam.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pageeditteam.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -203,12 +203,18 @@
 {
     initPage();
 
-    QRegExp pngSuffix("\\.png$");
+    m_playerHash = "0000000000000000000000000000000000000000";
+    m_loaded = false;
+}
 
-    m_playerHash = "0000000000000000000000000000000000000000";
+void PageEditTeam::lazyLoad()
+{
+    if(m_loaded) return;
+    m_loaded = true;
+    qDebug("[LAZYNESS] PageEditTeam::lazyLoad()");
 
+    QRegExp pngSuffix("\\.png$");
     DataManager & dataMgr = DataManager::instance();
-
     QStringList list;
 
 
@@ -236,7 +242,7 @@
             pix = pix.copy(0, 0, 32, 32);
         QIcon icon(pix);
 
-        QString grave = QString(file).remove(pngSuffix);
+        QString grave = file.remove(pngSuffix);
 
         CBGrave->addItem(icon, grave);
     }
@@ -327,6 +333,8 @@
 void PageEditTeam::createTeam(const QString & name, const QString & playerHash)
 {
     m_playerHash = playerHash;
+    lazyLoad();
+
     HWTeam newTeam(name);
     loadTeam(newTeam);
 }
@@ -334,6 +342,8 @@
 void PageEditTeam::editTeam(const QString & name, const QString & playerHash)
 {
     m_playerHash = playerHash;
+    lazyLoad();
+
     HWTeam team(name);
     team.loadFromFile();
     loadTeam(team);
--- a/QTfrontend/ui/page/pageeditteam.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pageeditteam.h	Mon Oct 28 14:07:04 2013 +0100
@@ -66,6 +66,7 @@
         HWTeam data();
         QString m_playerHash;
         KeyBinder * binder;
+        bool m_loaded;
 
         QLayout * bodyLayoutDefinition();
         QLayout * footerLayoutDefinition();
@@ -78,6 +79,8 @@
         QPushButton * btnRandomTeam;
         QPushButton * btnTestSound;
 
+        void lazyLoad();
+
     private slots:
         void saveTeam();
         void setRandomNames();
--- a/QTfrontend/ui/page/pageroomslist.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pageroomslist.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -531,17 +531,17 @@
 
 void PageRoomsList::onCreateClick()
 {
-    RoomNamePrompt prompt(parentWidget()->parentWidget(), m_gameSettings->value("frontend/lastroomname", QString()).toString());
-    connect(&prompt, SIGNAL(roomNameChosen(const QString &)), this, SLOT(onRoomNameChosen(const QString &)));
-    prompt.exec();
+    RoomNamePrompt prompt(this, m_gameSettings->value("frontend/lastroomname", QString()).toString());
+    if(prompt.exec())
+        onRoomNameChosen(prompt.getRoomName(), prompt.getPassword());
 }
 
-void PageRoomsList::onRoomNameChosen(const QString & roomName)
+void PageRoomsList::onRoomNameChosen(const QString & roomName, const QString & password)
 {
     if (!roomName.trimmed().isEmpty())
     {
         m_gameSettings->setValue("frontend/lastroomname", roomName);
-        emit askForCreateRoom(roomName);
+        emit askForCreateRoom(roomName, password);
     }
     else
     {
@@ -570,7 +570,7 @@
     if (!gameInLobby)
         emit askJoinConfirmation(roomName);
     else
-        emit askForJoinRoom(roomName);
+        emit askForJoinRoom(roomName, QString());
 }
 
 void PageRoomsList::onRefreshClick()
@@ -600,7 +600,7 @@
 
     if (reallyJoinMsg.exec() == QMessageBox::Ok)
     {
-        emit askForJoinRoom(room);
+        emit askForJoinRoom(room, QString());
     }
 }
 
--- a/QTfrontend/ui/page/pageroomslist.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pageroomslist.h	Mon Oct 28 14:07:04 2013 +0100
@@ -70,8 +70,8 @@
         void updateNickCounter(int cnt);
 
     signals:
-        void askForCreateRoom(const QString &);
-        void askForJoinRoom(const QString &);
+        void askForCreateRoom(const QString &, const QString &);
+        void askForJoinRoom(const QString &, const QString &);
         void askForRoomList();
         void askJoinConfirmation(const QString &);
 
@@ -89,7 +89,7 @@
         void onSortIndicatorChanged(int logicalIndex, Qt::SortOrder order);
         void onFilterChanged();
         void saveHeaderState();
-        void onRoomNameChosen(const QString &);
+        void onRoomNameChosen(const QString &, const QString &password);
         void roomSelectionChanged(const QModelIndex &, const QModelIndex &);
         void moveSelectionUp();
         void moveSelectionDown();
--- a/QTfrontend/ui/page/pagescheme.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pagescheme.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -383,6 +383,23 @@
     glBSLayout->addWidget(SB_GetAwayTime,14,2,1,1);
 
     l = new QLabel(gbBasicSettings);
+    l->setText(QLabel::tr("World Edge"));
+    l->setWordWrap(true);
+    glBSLayout->addWidget(l,15,0,1,1);
+    l = new QLabel(gbBasicSettings);
+    l->setFixedSize(32,32);
+    l->setPixmap(QPixmap(":/res/iconEarth.png"));
+    glBSLayout->addWidget(l,15,1,1,1);
+    CB_WorldEdge = new QComboBox(gbBasicSettings);
+    CB_WorldEdge->insertItem(0, tr("None (Default)"));
+    CB_WorldEdge->insertItem(1, tr("Wrap (World wraps)"));
+    CB_WorldEdge->insertItem(2, tr("Bounce (Edges reflect)"));
+    CB_WorldEdge->insertItem(3, tr("Sea (Edges connect to sea)"));
+    /* CB_WorldEdge->insertItem(4, tr("Skybox")); */
+    glBSLayout->addWidget(CB_WorldEdge,15,2,1,1);
+
+
+    l = new QLabel(gbBasicSettings);
     l->setText(QLabel::tr("Scheme Name:"));
 
     LE_name = new QLineEdit(this);
@@ -471,6 +488,7 @@
     mapper->addMapping(SB_HealthDecrease, 38);
     mapper->addMapping(SB_RopeModifier, 39);
     mapper->addMapping(SB_GetAwayTime, 40);
+    mapper->addMapping(CB_WorldEdge, 41, "currentIndex");
 
     mapper->toFirst();
 }
--- a/QTfrontend/ui/page/pagescheme.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/page/pagescheme.h	Mon Oct 28 14:07:04 2013 +0100
@@ -91,6 +91,7 @@
         QSpinBox * SB_Explosives;
         QSpinBox * SB_RopeModifier;
         QSpinBox * SB_GetAwayTime;
+        QComboBox * CB_WorldEdge;
         QLineEdit * LE_name;
 
         QGroupBox * gbGameModes;
--- a/QTfrontend/ui/widget/chatwidget.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/chatwidget.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -930,3 +930,17 @@
         chatText->verticalScrollBar()->setValue(m_scrollBarPos);
     }
 }
+
+void HWChatWidget::resizeEvent(QResizeEvent * event)
+{
+    Q_UNUSED(event);
+
+    afterContentAdd();
+}
+
+void HWChatWidget::showEvent(QShowEvent * event)
+{
+    Q_UNUSED(event);
+
+     afterContentAdd();
+}
--- a/QTfrontend/ui/widget/chatwidget.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/chatwidget.h	Mon Oct 28 14:07:04 2013 +0100
@@ -68,6 +68,8 @@
     protected:
         virtual void dragEnterEvent(QDragEnterEvent * event);
         virtual void dropEvent(QDropEvent * event);
+        virtual void resizeEvent(QResizeEvent * event);
+        virtual void showEvent(QShowEvent * event);
 
     private:
         static QString * s_styleSheet;
--- a/QTfrontend/ui/widget/drawmapwidget.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/drawmapwidget.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -123,6 +123,11 @@
     if(m_scene) m_scene->setErasing(erasing);
 }
 
+void DrawMapWidget::setPathType(DrawMapScene::PathType pathType)
+{
+    if(m_scene) m_scene->setPathType(pathType);
+}
+
 void DrawMapWidget::save(const QString & fileName)
 {
     if(m_scene)
@@ -160,6 +165,7 @@
         }
         else
             m_scene->decode(qUncompress(QByteArray::fromBase64(f.readAll())));
+            //m_scene->decode(f.readAll());
     }
 }
 
@@ -191,7 +197,7 @@
     QGraphicsView::setScene(scene);
 }
 
-// Why don't I ever recieve this event?
+// Why don't I ever receive this event?
 void DrawMapView::enterEvent(QEvent *event)
 {
     if(m_scene)
--- a/QTfrontend/ui/widget/drawmapwidget.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/drawmapwidget.h	Mon Oct 28 14:07:04 2013 +0100
@@ -100,6 +100,7 @@
         void setErasing(bool erasing);
         void save(const QString & fileName);
         void load(const QString & fileName);
+        void setPathType(DrawMapScene::PathType pathType);
 
     protected:
         void changeEvent(QEvent *e);
--- a/QTfrontend/ui/widget/gamecfgwidget.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/gamecfgwidget.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -321,6 +321,7 @@
     bcfg << QString("e$healthdec %1").arg(schemeData(38).toInt()).toUtf8();
     bcfg << QString("e$ropepct %1").arg(schemeData(39).toInt()).toUtf8();
     bcfg << QString("e$getawaytime %1").arg(schemeData(40).toInt()).toUtf8();
+    bcfg << QString("e$worldedge %1").arg(schemeData(41).toInt()).toUtf8();
     bcfg << QString("e$template_filter %1").arg(pMapContainer->getTemplateFilter()).toUtf8();
     bcfg << QString("e$mapgen %1").arg(mapgen).toUtf8();
 
--- a/QTfrontend/ui/widget/roomnameprompt.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/roomnameprompt.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -23,6 +23,7 @@
 #include <QLineEdit>
 #include <QLabel>
 #include <QDebug>
+#include <QCheckBox>
 
 #include "roomnameprompt.h"
 
@@ -32,24 +33,34 @@
     setWindowFlags(Qt::Sheet);
     setWindowModality(Qt::WindowModal);
     setMinimumSize(360, 130);
-    resize(360, 130);
+    resize(360, 180);
     setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
 
     // Layout
     QVBoxLayout * dialogLayout = new QVBoxLayout(this);
 
     // Label
-    label = new QLabel(tr("Enter a name for your room."));
+    label = new QLabel(tr("Enter a name for your room."), this);
     label->setWordWrap(true);
-    dialogLayout->addWidget(label, 0);
+    dialogLayout->addWidget(label);
 
     // Input box
-    editBox = new QLineEdit();
-    editBox->setText(roomName);
-    editBox->setMaxLength(59); // It didn't like 60 :(
-    editBox->setStyleSheet("QLineEdit { padding: 3px; }");
-    editBox->selectAll();
-    dialogLayout->addWidget(editBox, 1);
+    leRoomName = new QLineEdit(this);
+    leRoomName->setText(roomName);
+    leRoomName->setMaxLength(59); // It didn't like 60 :(
+    leRoomName->setStyleSheet("QLineEdit { padding: 3px; }");
+    leRoomName->selectAll();
+    dialogLayout->addWidget(leRoomName);
+
+    cbSetPassword = new QCheckBox(this);
+    cbSetPassword->setText(tr("set password"));
+    dialogLayout->addWidget(cbSetPassword);
+
+    lePassword = new QLineEdit(this);
+    lePassword->setMaxLength(30);
+    lePassword->setStyleSheet("QLineEdit { padding: 3px; }");
+    lePassword->setEnabled(false);
+    dialogLayout->addWidget(lePassword);
 
     dialogLayout->addStretch(1);
 
@@ -73,10 +84,20 @@
 
     setStyleSheet("QPushButton { padding: 5px; }");
 
-    connect(btnOkay, SIGNAL(clicked()), this, SLOT(setRoomName()));
+    connect(cbSetPassword, SIGNAL(toggled(bool)), this, SLOT(checkBoxToggled()));
+}
+
+QString RoomNamePrompt::getRoomName()
+{
+    return leRoomName->text();
 }
 
-void RoomNamePrompt::setRoomName()
+QString RoomNamePrompt::getPassword()
 {
-    emit roomNameChosen(editBox->text());
+    return lePassword->text();
 }
+
+void RoomNamePrompt::checkBoxToggled()
+{
+    lePassword->setEnabled(cbSetPassword->isChecked());
+}
--- a/QTfrontend/ui/widget/roomnameprompt.h	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/roomnameprompt.h	Mon Oct 28 14:07:04 2013 +0100
@@ -23,6 +23,7 @@
 
 class QLineEdit;
 class QLabel;
+class QCheckBox;
 
 class RoomNamePrompt : public QDialog
 {
@@ -30,16 +31,17 @@
 
     public:
         RoomNamePrompt(QWidget* parent, const QString & roomName);
+        QString getRoomName();
+        QString getPassword();
 
-    signals:
-        void roomNameChosen(const QString & roomName);
+    private:
+        QLineEdit * leRoomName;
+        QLabel * label;
+        QCheckBox * cbSetPassword;
+        QLineEdit * lePassword;
 
     private slots:
-        void setRoomName();
-
-    private:
-        QLineEdit * editBox;
-        QLabel * label;
+        void checkBoxToggled();
 };
 
 #endif // ROOMNAMEPROMPT_H
--- a/QTfrontend/ui/widget/teamselhelper.cpp	Wed Sep 25 05:42:16 2013 +0300
+++ b/QTfrontend/ui/widget/teamselhelper.cpp	Mon Oct 28 14:07:04 2013 +0100
@@ -55,7 +55,7 @@
 
     butt = new QPushButton(difficultyIcon, team.name().replace("&","&&"), this);
     butt->setFlat(true);
-    butt->setWhatsThis(tr("%1's team").arg(team.owner()));
+    butt->setToolTip(team.owner());
     mainLayout.addWidget(butt);
     butt->setStyleSheet("QPushButton{"
                         "icon-size: 48px;"
--- a/cmake_modules/CMakeDeterminePascalCompiler.cmake	Wed Sep 25 05:42:16 2013 +0300
+++ b/cmake_modules/CMakeDeterminePascalCompiler.cmake	Mon Oct 28 14:07:04 2013 +0100
@@ -5,6 +5,9 @@
 # the cmake variable CMAKE_GENERATOR_PASCAL which can be defined by a generator
 # as a default compiler
 
+# NOTE: on Darwin cmake >= 2.8.11 until cmake <= 2.8.12.1 will add an incompatible
+# -F flag to <FLAGS> so you won't be able to use those versions with this script
+
 if(NOT CMAKE_Pascal_COMPILER)
     # prefer the environment variable FPC
     if($ENV{FPC} MATCHES ".+")
--- a/cmake_modules/CMakePascalInformation.cmake	Wed Sep 25 05:42:16 2013 +0300
+++ b/cmake_modules/CMakePascalInformation.cmake	Mon Oct 28 14:07:04 2013 +0100
@@ -2,6 +2,7 @@
 # It also loads the available platform file for the system-compiler
 # if it exists.
 
+# in case fpc ever becomes included in cmake
 get_filename_component(CMAKE_BASE_NAME ${CMAKE_Pascal_COMPILER} NAME_WE)
 set(CMAKE_SYSTEM_AND_Pascal_COMPILER_INFO_FILE
     ${CMAKE_ROOT}/Modules/Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME}.cmake)
@@ -33,17 +34,18 @@
 
 # No flags supported during linking as a shell script takes care of it
 # however to avoid interferences we escape -Wl flags to the Pascal -k
-if(NOT CMAKE_SHARED_LIBRARY_CREATE_Pascal_FLAGS)
-#-shared
-    string(REGEX REPLACE "-Wl," "-k" CMAKE_SHARED_LIBRARY_CREATE_Pascal_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
-endif(NOT CMAKE_SHARED_LIBRARY_CREATE_Pascal_FLAGS)
+#if(NOT CMAKE_SHARED_LIBRARY_CREATE_Pascal_FLAGS)
+#-shared (linux) / -dynamiclib -Wl,-headerpad_max_install_names (darwin)
+#    string(REGEX REPLACE "-Wl," "-k" CMAKE_SHARED_LIBRARY_CREATE_Pascal_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
+#endif(NOT CMAKE_SHARED_LIBRARY_CREATE_Pascal_FLAGS)
 
 if(NOT CMAKE_SHARED_LIBRARY_Pascal_FLAGS AND CMAKE_SHARED_LIBRARY_C_FLAGS)
+#-fPIC
     string(REGEX REPLACE "-Wl," "-k" CMAKE_SHARED_LIBRARY_Pascal_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS})
 endif()
 
 if(NOT CMAKE_SHARED_LIBRARY_LINK_Pascal_FLAGS AND CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
-#-rdynamic
+#-rdynamic (linux) / (empty on darwin)
     string(REGEX REPLACE "-Wl," "-k" CMAKE_SHARED_LIBRARY_LINK_Pascal_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS})
 endif()
 
@@ -70,9 +72,10 @@
 endif(NOT CMAKE_MODULE_EXISTS)
 
 # repeat for modules
-if(NOT CMAKE_SHARED_MODULE_CREATE_Pascal_FLAGS)
+if(NOT CMAKE_SHARED_MODULE_CREATE_Pascal_FLAGS AND CMAKE_SHARED_MODULE_CREATE_C_FLAGS)
+# ? (linux) / -bundle -Wl,-headerpad_max_install_names (darwin)
     string(REGEX REPLACE "-Wl," "-k" CMAKE_SHARED_MODULE_CREATE_Pascal_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS})
-endif(NOT CMAKE_SHARED_MODULE_CREATE_Pascal_FLAGS)
+endif()
 
 if(NOT CMAKE_SHARED_MODULE_Pascal_FLAGS AND CMAKE_SHARED_MODULE_C_FLAGS)
     string(REGEX REPLACE "-Wl," "-k" CMAKE_SHARED_MODULE_Pascal_FLAGS ${CMAKE_SHARED_MODULE_C_FLAGS})
@@ -86,6 +89,7 @@
     set(CMAKE_SHARED_MODULE_RUNTIME_Pascal_FLAG_SEP ${CMAKE_SHARED_MODULE_RUNTIME_C_FLAG_SEP})
 endif(NOT CMAKE_SHARED_MODULE_RUNTIME_Pascal_FLAG_SEP)
 
+# now other system things
 if(NOT CMAKE_INCLUDE_FLAG_Pascal)
     #amazing, fpc: -I<x>  Add <x> to include path
     set(CMAKE_INCLUDE_FLAG_Pascal ${CMAKE_INCLUDE_FLAG_C})
@@ -95,8 +99,14 @@
     set(CMAKE_INCLUDE_FLAG_SEP_Pascal ${CMAKE_INCLUDE_FLAG_SEP_C})
 endif(NOT CMAKE_INCLUDE_FLAG_SEP_Pascal)
 
+if(NOT CMAKE_Pascal_FRAMEWORK_SEARCH_FLAG)
+    #however -F won't work, -Ff is Pascal equivalent
+    set(CMAKE_Pascal_FRAMEWORK_SEARCH_FLAG "-Ff")
+endif(NOT CMAKE_Pascal_FRAMEWORK_SEARCH_FLAG)
+
 # Copy C version of this flag which is normally determined in platform file.
 if(NOT CMAKE_SHARED_LIBRARY_SONAME_Pascal_FLAG)
+#-soname (linux) / -install-name (dawin)
     set(CMAKE_SHARED_LIBRARY_SONAME_Pascal_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG})
 endif(NOT CMAKE_SHARED_LIBRARY_SONAME_Pascal_FLAG)
 
--- a/cmake_modules/platform.cmake	Wed Sep 25 05:42:16 2013 +0300
+++ b/cmake_modules/platform.cmake	Mon Oct 28 14:07:04 2013 +0100
@@ -1,5 +1,10 @@
 
 if(APPLE)
+    if(${CMAKE_VERSION} VERSION_GREATER "2.8.10.2" AND
+       ${CMAKE_VERSION} VERSION_LESS "2.8.12.1")
+        message(FATAL_ERROR "This version of CMake is known *not* to work, please update or use a lower version")
+    endif()
+
     set(CMAKE_FIND_FRAMEWORK "FIRST")
 
     #what system are we building for
--- a/gameServer/Actions.hs	Wed Sep 25 05:42:16 2013 +0300
+++ b/gameServer/Actions.hs	Mon Oct 28 14:07:04 2013 +0100
@@ -456,6 +456,7 @@
 
 processAction JoinLobby = do
     chan <- client's sendChan
+    rnc <- gets roomsClients
     clientNick <- client's nick
     isAuthenticated <- liftM (not . B.null) $ client's webPassword
     isAdmin <- client's isAdministrator
@@ -465,6 +466,10 @@
     let authenticatedNicks = L.map nick . L.filter (not . B.null . webPassword) $ loggedInClients
     let adminsNicks = L.map nick . L.filter isAdministrator $ loggedInClients
     let contrNicks = L.map nick . L.filter isContributor $ loggedInClients
+    inRoomNicks <- io $
+        allClientsM rnc
+        >>= filterM (liftM ((/=) lobbyId) . clientRoomM rnc)
+        >>= mapM (client'sM rnc nick)
     let clFlags = B.concat . L.concat $ [["u" | isAuthenticated], ["a" | isAdmin], ["c" | isContr]]
     mapM_ processAction . concat $ [
         [AnswerClients clientsChans ["LOBBY:JOINED", clientNick]]
@@ -472,6 +477,7 @@
         , [AnswerClients [chan] ("CLIENT_FLAGS" : "+u" : authenticatedNicks) | not $ null authenticatedNicks]
         , [AnswerClients [chan] ("CLIENT_FLAGS" : "+a" : adminsNicks) | not $ null adminsNicks]
         , [AnswerClients [chan] ("CLIENT_FLAGS" : "+c" : contrNicks) | not $ null contrNicks]
+        , [AnswerClients [chan] ("CLIENT_FLAGS" : "+i" : inRoomNicks) | not $ null inRoomNicks]
         , [AnswerClients (chan : clientsChans) ["CLIENT_FLAGS",  B.concat["+" , clFlags], clientNick] | not $ B.null clFlags]
         , [ModifyClient (\cl -> cl{logonPassed = True, isVisible = True})]
         , [SendServerMessage]
--- a/gameServer/CoreTypes.hs	Wed Sep 25 05:42:16 2013 +0300
+++ b/gameServer/CoreTypes.hs	Mon Oct 28 14:07:04 2013 +0100
@@ -101,7 +101,6 @@
         logonPassed :: Bool,
         isVisible :: Bool,
         clientProto :: !Word16,
-        roomID :: RoomIndex,
         pingsQueue :: !Word,
         isMaster :: Bool,
         isReady :: !Bool,
--- a/gameServer/NetRoutines.hs	Wed Sep 25 05:42:16 2013 +0300
+++ b/gameServer/NetRoutines.hs	Mon Oct 28 14:07:04 2013 +0100
@@ -36,7 +36,6 @@
                     False
                     False
                     0
-                    lobbyId
                     0
                     False
                     False
--- a/gameServer/OfficialServer/checker.hs	Wed Sep 25 05:42:16 2013 +0300
+++ b/gameServer/OfficialServer/checker.hs	Mon Oct 28 14:07:04 2013 +0100
@@ -171,7 +171,7 @@
 
     Right (login, password) <- runErrorT $ do
         d <- liftIO $ getHomeDirectory
-        conf <- join . liftIO . CF.readfile CF.emptyCP $ d ++ "/.hedgewars/hedgewars.ini"
+        conf <- join . liftIO . CF.readfile CF.emptyCP $ d ++ "/.hedgewars/settings.ini"
         l <- CF.get conf "net" "nick"
         p <- CF.get conf "net" "passwordhash"
         return (B.pack l, B.pack p)
--- a/hedgewars/hwengine.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/hwengine.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -32,7 +32,7 @@
 uses SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler
      , uSound, uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uAILandMarks, uLandTexture, uCollisions
      , SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted
-     , uPhysFSLayer, uCursor, uRandom, ArgParsers, uVisualGearsHandlers
+     , uPhysFSLayer, uCursor, uRandom, ArgParsers, uVisualGearsHandlers, uTextures
      {$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF}
      {$IFDEF USE_TOUCH_INTERFACE}, uTouch {$ENDIF}
      {$IFDEF ANDROID}, GLUnit{$ENDIF}
@@ -441,9 +441,10 @@
     if complete then
     begin
         uPhysFSLayer.initModule;
+        uTextures.initModule;
 {$IFDEF ANDROID}GLUnit.initModule;{$ENDIF}
 {$IFDEF USE_TOUCH_INTERFACE}uTouch.initModule;{$ENDIF}
-{$IFDEF USE_VIDEO_RECORDING}uVideoRec.initModule;{$ENDIF}   //stub
+{$IFDEF USE_VIDEO_RECORDING}uVideoRec.initModule;{$ENDIF}
         uAI.initModule;
         uAIMisc.initModule;
         uAILandMarks.initModule;    //stub
@@ -453,7 +454,7 @@
         uChat.initModule;
         uCollisions.initModule;
         uGears.initModule;
-        uInputHandler.initModule;   //stub
+        uInputHandler.initModule;
         uMisc.initModule;
         uLandTexture.initModule;    //stub
         uScript.initModule;
@@ -493,6 +494,7 @@
 {$IFDEF USE_VIDEO_RECORDING}uVideoRec.freeModule;{$ENDIF}
 {$IFDEF USE_TOUCH_INTERFACE}uTouch.freeModule;{$ENDIF}  //stub
 {$IFDEF ANDROID}GLUnit.freeModule;{$ENDIF}
+        uTextures.freeModule;
         uPhysFSLayer.freeModule;
     end;
 
--- a/hedgewars/uAI.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uAI.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -106,7 +106,7 @@
 procedure TestAmmos(var Actions: TActions; Me: PGear; rareChecks: boolean);
 var BotLevel: Byte;
     ap: TAttackParams;
-    Score, i, dAngle: LongInt;
+    Score, i, t, n, dAngle: LongInt;
     a, aa: TAmmoType;
 begin
 BotLevel:= Me^.Hedgehog^.BotLevel;
@@ -182,7 +182,15 @@
                         end else
                         if (Ammoz[a].Ammo.Propz and ammoprop_AttackingPut) = 0 then
                             begin
+                            if (AmmoTests[a].flags and amtest_MultipleAttacks) = 0 then
+                                n:= 1 else n:= ap.AttacksNum;
+
                             AddAction(BestActions, aia_attack, aim_push, 650 + random(300), 0, 0);
+                            for t:= 2 to n do
+                                begin
+                                AddAction(BestActions, aia_attack, aim_push, 150, 0, 0);
+                                AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0);
+                                end;
                             AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0);
                             end;
 
--- a/hedgewars/uAIAmmoTests.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uAIAmmoTests.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -22,13 +22,14 @@
 interface
 uses uConsts, uFloat, uTypes, uAIMisc;
 const
-    amtest_Rare     = $00000001; // check only several positions
-    amtest_NoTarget = $00000002; // each pos, but no targetting
+    amtest_Rare            = $00000001; // check only several positions
+    amtest_NoTarget        = $00000002; // each pos, but no targetting
+    amtest_MultipleAttacks = $00000004; // test could result in multiple attacks, set AttacksNum
 
 var windSpeed: real;
 
 type TAttackParams = record
-        Time: Longword;
+        Time, AttacksNum: Longword;
         Angle, Power: LongInt;
         ExplX, ExplY, ExplR: LongInt;
         AttackPutX, AttackPutY: LongInt;
@@ -72,7 +73,7 @@
             (proc: nil;              flags: 0), // amSkip
             (proc: nil;              flags: 0), // amRope
             (proc: nil;              flags: 0), // amMine
-            (proc: @TestDesertEagle; flags: 0), // amDEagle
+            (proc: @TestDesertEagle; flags: amtest_MultipleAttacks), // amDEagle
             (proc: nil;              flags: 0), // amDynamite
             (proc: @TestFirePunch;   flags: amtest_NoTarget), // amFirePunch
             (proc: @TestWhip;        flags: amtest_NoTarget), // amWhip
@@ -715,8 +716,13 @@
     or (d > 48);
 
 if Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) < 5 then
-     valueResult:= RateShove(Me, Targ.Point.X, Targ.Point.Y, 1, 7, 20, vX*0.125, vY*0.125, afTrackFall)
-else valueResult:= BadTurn;
+    begin
+    ap.AttacksNum:= 1 + (d + 8) div 12;
+    valueResult:= RateShove(Me, Targ.Point.X, Targ.Point.Y, 1, 7, 20, vX*0.125, vY*0.125, afTrackFall) - ap.AttacksNum
+    end
+else
+    valueResult:= BadTurn;
+
 TestDesertEagle:= valueResult
 end;
 
--- a/hedgewars/uChat.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uChat.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -41,7 +41,7 @@
     Width: LongInt;
     s: shortstring;
     end;
-    TChatCmd = (quit, pause, finish, fullscreen);
+    TChatCmd = (quit, pause, finish, showhistory, fullscreen);
 
 var Strs: array[0 .. MaxStrIndex] of TChatLine;
     MStrs: array[0 .. MaxStrIndex] of shortstring;
@@ -73,6 +73,7 @@
             (ChatCmd: '/quit'; ProcedureCallChatCmd: 'halt'),
             (ChatCmd: '/pause'; ProcedureCallChatCmd: 'pause'),
             (ChatCmd: '/finish'; ProcedureCallChatCmd: 'finish'),
+            (ChatCmd: '/history'; ProcedureCallChatCmd: 'history'),
             (ChatCmd: '/fullscreen'; ProcedureCallChatCmd: 'fullscr')
             );
 
--- a/hedgewars/uCommandHandlers.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uCommandHandlers.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -797,6 +797,11 @@
   CampaignVariable := s;
 end;
 
+procedure chWorldEdge(var s: shortstring);
+begin
+WorldEdge:= TWorldEdge(StrToInt(s))
+end;
+
 procedure initModule;
 begin
 //////// Begin top sorted by freq analysis not including chatmsg
@@ -882,6 +887,7 @@
     RegisterVariable('-cur_r'  , @chCurR_m       , true );
     RegisterVariable('campvar' , @chCampVar      , true );
     RegisterVariable('record'  , @chRecord       , true );
+    RegisterVariable('worldedge',@chWorldEdge    , false);
 end;
 
 procedure freeModule;
--- a/hedgewars/uConsts.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uConsts.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -250,6 +250,8 @@
     ammoprop_NeedTarget   = $00000004;
     ammoprop_ForwMsgs     = $00000008;
     ammoprop_AttackInMove = $00000010;
+    ammoprop_DoesntStopTimerWhileAttacking
+                          = $00000020;
     ammoprop_NoCrosshair  = $00000040;
     ammoprop_AttackingPut = $00000080;
     ammoprop_DontHold     = $00000100;
@@ -263,6 +265,8 @@
     ammoprop_OscAim       = $00010000;
     ammoprop_NoMoveAfter  = $00020000;
     ammoprop_Track        = $00040000;
+    ammoprop_DoesntStopTimerInMultiShoot
+                          = $00080000;
     ammoprop_NoRoundEnd   = $10000000;
 
     AMMO_INFINITE = 100;
--- a/hedgewars/uGears.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uGears.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -401,7 +401,8 @@
             if (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.State and gstAttacked = 0)
             and (CurAmmoGear = nil) then
                 SweepDirty;
-            CheckNoDamage;
+            if (CurrentHedgehog^.Gear = nil) or (CurrentHedgehog^.Gear^.State and gstHHDriven = 0) or (CurrentHedgehog^.Gear^.Damage = 0) then
+                CheckNoDamage;
             AliveCount:= 0; // shorter version of check for win to allow typical step activity to proceed
             for i:= 0 to Pred(ClansCount) do
                 if ClansArray[i]^.ClanHealth > 0 then
@@ -419,8 +420,10 @@
 
 if TurnTimeLeft > 0 then
     if CurrentHedgehog^.Gear <> nil then
-        if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0) and
-            not(isInMultiShoot and (CurrentHedgehog^.CurAmmoType in [amShotgun, amDEagle, amSniperRifle])) then
+        if (((CurrentHedgehog^.Gear^.State and gstAttacking) = 0)
+            or (Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_DoesntStopTimerWhileAttacking <> 0))
+            and not(isInMultiShoot and ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_DoesntStopTimerInMultiShoot) <> 0)) then
+            //(CurrentHedgehog^.CurAmmoType in [amShotgun, amDEagle, amSniperRifle])
                 begin
                 if (TurnTimeLeft = 5000)
                 and (cHedgehogTurnTime >= 10000)
--- a/hedgewars/uGearsHandlersMess.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uGearsHandlersMess.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -280,10 +280,18 @@
 var
     isFalling: boolean;
     //tmp: QWord;
-    tdX, tdY: hwFloat;
+    tX, tdX, tdY: hwFloat;
     collV, collH: LongInt;
     land: word;
 begin
+    tX:= Gear^.X;
+    if (Gear^.Kind <> gtGenericFaller) and WorldWrap(Gear) and (WorldEdge = weWrap) and (Gear^.AdvBounce <> 0) and
+      (TestCollisionXwithGear(Gear, 1) or TestCollisionXwithGear(Gear, -1))  then
+        begin
+        Gear^.X:= tX;
+        Gear^.dX.isNegative:= (hwRound(tX) > leftX+Gear^.Radius*2)
+        end;
+
     // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems.
     if Gear^.dX.Round > 2 then
         Gear^.dX.QWordValue:= 8589934592;
@@ -302,8 +310,6 @@
     tdX := Gear^.dX;
     tdY := Gear^.dY;
 
-
-
 // might need some testing/adjustments - just to avoid projectiles to fly forever (accelerated by wind/skips)
     if (hwRound(Gear^.X) < min(LAND_WIDTH div -2, -2048))
     or (hwRound(Gear^.X) > max(LAND_WIDTH * 3 div 2, 6144)) then
@@ -384,8 +390,7 @@
 
     Gear^.X := Gear^.X + Gear^.dX;
     Gear^.Y := Gear^.Y + Gear^.dY;
-    if Gear^.Kind <> gtBee then
-        CheckGearDrowning(Gear);
+    CheckGearDrowning(Gear);
     //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and
     if (not isFalling) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) then
         Gear^.State := Gear^.State and (not gstMoving)
@@ -695,6 +700,13 @@
         draw:= true;
     xx:= hwRound(Gear^.X);
     yy:= hwRound(Gear^.Y);
+    if draw and (WorldEdge = weWrap) and ((xx < leftX+3) or (xx > rightX-3)) then
+        begin
+        if xx < leftX+3 then 
+             xx:= rightX-3
+        else xx:= leftX+3;
+        Gear^.X:= int2hwFloat(xx)
+        end
     end
 else if GameTicks and $7 = 0 then
     begin
@@ -889,6 +901,7 @@
     flower: PVisualGear;
 
 begin
+    WorldWrap(Gear);
     AllInactive := false;
     gX := hwRound(Gear^.X);
     gY := hwRound(Gear^.Y);
@@ -968,6 +981,7 @@
         dec(Gear^.Timer)
     else
         begin
+        Gear^.State:= Gear^.State and not gstSubmersible;
         if nuw then
            begin
             StopSoundChan(Gear^.SoundChannel);
@@ -1055,6 +1069,7 @@
     repeat
         Gear^.X := Gear^.X + Gear^.dX;
         Gear^.Y := Gear^.Y + Gear^.dY;
+        WorldWrap(Gear);
         CheckCollision(Gear);
         if (Gear^.State and gstCollision) <> 0 then
             begin
@@ -1120,7 +1135,7 @@
 procedure doStepBulletWork(Gear: PGear);
 var
     i, x, y: LongWord;
-    oX, oY: hwFloat;
+    oX, oY, tX, tY, cX, cY: hwFloat;
     VGear: PVisualGear;
 begin
     AllInactive := false;
@@ -1131,6 +1146,22 @@
     repeat
         Gear^.X := Gear^.X + Gear^.dX;
         Gear^.Y := Gear^.Y + Gear^.dY;
+        tX:= Gear^.X;
+        tY:= Gear^.Y;
+        if (Gear^.PortalCounter < 30) and WorldWrap(Gear) then
+            begin
+            cX:= Gear^.X;
+            cY:= Gear^.Y;
+            Gear^.X:= tX;
+            Gear^.Y:= tY;
+            SpawnBulletTrail(Gear);
+            Gear^.X:= cX;
+            Gear^.Y:= cY;
+            inc(Gear^.PortalCounter);
+            Gear^.Elasticity:= Gear^.X;
+            Gear^.Friction:= Gear^.Y;
+            SpawnBulletTrail(Gear);
+            end;
         x := hwRound(Gear^.X);
         y := hwRound(Gear^.Y);
 
@@ -1305,6 +1336,7 @@
     HHGear: PGear;
 begin
     AllInactive := false;
+    WorldWrap(Gear);
     HHGear := Gear^.Hedgehog^.Gear;
     dec(Gear^.Timer);
     if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then
@@ -1428,6 +1460,7 @@
     prevX: LongInt;
 begin
     AllInactive := false;
+    WorldWrap(Gear);
     dec(Gear^.Timer);
     if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then
         dec(TurnTimeLeft);
@@ -2013,6 +2046,7 @@
     tdX,tdY: HWFloat;
     landPixel: Word;
 begin
+    WorldWrap(Gear);
     sticky:= (Gear^.State and gsttmpFlag) <> 0;
     if not sticky then AllInactive := false;
 
@@ -2378,6 +2412,7 @@
 var
     HHGear: PGear;
     x, y, tx, ty: hwFloat;
+    rx: LongInt;
 begin
     AllInactive := false;
 
@@ -2386,8 +2421,13 @@
     ty := int2hwFloat(Gear^.Target.Y);
     x := HHGear^.X;
     y := HHGear^.Y;
-
-    if (Distance(tx - x, ty - y) > _256)
+    rx:= hwRound(x);
+
+    if ((Distance(tx - x, ty - y) > _256) and ((WorldEdge <> weWrap) or 
+            (
+            (Distance(tx - int2hwFloat(rightX+(rx-leftX)), ty - y) > _256) and
+            (Distance(tx - int2hwFloat(leftX-(rightX-rx)), ty - y) > _256)
+            )))
     or (not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprAmGirder].Width div 2, Gear^.Target.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true, false)) then
         begin
         PlaySound(sndDenied);
@@ -3187,11 +3227,26 @@
 var
     HHGear: PGear;
     i: LongInt;
-    dX, dY: hwFloat;
+    dX, dY, X, Y : hwFloat;
     fChanged: boolean;
     trueAngle: Longword;
     t: PGear;
 begin
+    if WorldWrap(Gear) and (WorldEdge <> weWrap) then
+        begin
+        Y.isNegative:= false;
+        Y.QWordValue:= 4294967296 * 112;
+        X.isNegative:= false;
+        X.QWordValue:= 4294967296 * 35;
+        dX.isNegative:= false;
+        dX.QWordValue:= 4294967296 * 1152;
+
+        dY:=hwAbs(Gear^.dX*4);
+        dY:= dY + hwPow(dY,3)/_6 + _3 * hwPow(dY,5) / _40 + _5 * hwPow(dY,7) / Y + X * hwPow(dY,9) / dX;
+        Gear^.Angle:= hwRound(dY*_2048 / _PI);
+        if not Gear^.dY.isNegative then Gear^.Angle:= 2048-Gear^.Angle;
+        if Gear^.dX.isNegative then Gear^.Angle:= 4096-Gear^.Angle;
+        end;
     AllInactive := false;
 
     HHGear := Gear^.Hedgehog^.Gear;
@@ -3201,7 +3256,7 @@
         dec(Gear^.Timer);
 
     fChanged := false;
-    if ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then
+    if (HHGear = nil) or ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then
         begin
         fChanged := true;
         if Gear^.Angle > 2048 then
@@ -3246,7 +3301,7 @@
     else
         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
 
-    if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then
+    if (HHGear <> nil) and ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then
         begin
         HHGear^.Message := HHGear^.Message and (not gmAttack);
         AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, Gear^.dX * _0_5, Gear^.dY *
@@ -3254,7 +3309,7 @@
         dec(Gear^.Health)
         end;
 
-    if ((HHGear^.Message and gmLJump) <> 0) and ((Gear^.State and gsttmpFlag) = 0) then
+    if (HHGear <> nil) and ((HHGear^.Message and gmLJump) <> 0) and ((Gear^.State and gsttmpFlag) = 0) then
         begin
         Gear^.State := Gear^.State or gsttmpFlag;
         PauseMusic;
@@ -4044,7 +4099,8 @@
         if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil)
         and (iterator = CurrentHedgehog^.Gear)
         and (CurAmmoGear <> nil)
-        and (CurAmmoGear^.Kind =gtRope) then
+        and (CurAmmoGear^.Kind = gtRope)
+        and (CurAmmoGear^.Elasticity <> _0) then
                CurAmmoGear^.PortalCounter:= 1;
 
         if not isbullet and (iterator^.State and gstInvisible = 0)
@@ -4097,6 +4153,7 @@
     x, y, tx, ty: LongInt;
     s: hwFloat;
 begin
+    WorldWrap(Gear);
     x := hwRound(Gear^.X);
     y := hwRound(Gear^.Y);
     tx := 0;
@@ -4637,6 +4694,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepPoisonCloud(Gear: PGear);
 begin
+    WorldWrap(Gear);
     if Gear^.Timer = 0 then
         begin
         DeleteGear(Gear);
@@ -5117,11 +5175,12 @@
     begin
     cnt:= 0;
     for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do
-        for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do
-            if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil)
-            and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0)
-            and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then
-                inc(cnt);
+        with HH^.Team^.Clan^.Teams[j]^ do
+            for i:= 0 to Pred(HedgehogsNumber) do
+                if (Hedgehogs[i].Gear <> nil)
+                and ((Hedgehogs[i].Gear^.State and gstDrowning) = 0)
+                and (Hedgehogs[i].Gear^.Health > Hedgehogs[i].Gear^.Damage) then
+                    inc(cnt);
     if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then
         begin
         if HH^.GearHidden <> nil then
--- a/hedgewars/uGearsHandlersRope.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uGearsHandlersRope.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -31,8 +31,17 @@
 procedure doStepRopeAfterAttack(Gear: PGear);
 var 
     HHGear: PGear;
+    tX:     hwFloat;
 begin
     HHGear := Gear^.Hedgehog^.Gear;
+    tX:= HHGear^.X;
+    if WorldWrap(HHGear) and (WorldEdge = weWrap) and 
+       (TestCollisionXwithGear(HHGear, 1) or TestCollisionXwithGear(HHGear, -1))  then
+        begin
+        HHGear^.X:= tX;
+        HHGear^.dX.isNegative:= (hwRound(tX) > leftX+HHGear^.Radius*2)
+        end;
+
     if (HHGear^.Hedgehog^.CurAmmoType = amParachute) and (HHGear^.dY > _0_39) then
         begin
         DeleteGear(Gear);
@@ -116,8 +125,20 @@
 
     HHGear := Gear^.Hedgehog^.Gear;
 
-    if ((HHGear^.State and gstHHDriven) = 0)
-       or (CheckGearDrowning(HHGear)) or (Gear^.PortalCounter <> 0) then
+    tX:= HHGear^.X;
+    if WorldWrap(HHGear) and (WorldEdge = weWrap) and 
+       (TestCollisionXwithGear(HHGear, 1) or TestCollisionXwithGear(HHGear, -1))  then
+        begin
+        PlaySound(sndRopeRelease);
+        RopeDeleteMe(Gear, HHGear);
+        HHGear^.X:= tX;
+        HHGear^.dX.isNegative:= (hwRound(tX) > leftX+HHGear^.Radius*2);
+        exit
+        end;
+
+    tX:= HHGear^.X;
+    if ((HHGear^.State and gstHHDriven) = 0) or
+        (CheckGearDrowning(HHGear)) or (Gear^.PortalCounter <> 0) then
         begin
         PlaySound(sndRopeRelease);
         RopeDeleteMe(Gear, HHGear);
--- a/hedgewars/uGearsHedgehog.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uGearsHedgehog.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -116,7 +116,7 @@
     // Try again in the next slot
     if CurAmmoType = prevAmmo then
         begin
-        if slot >= cMaxSlotIndex then slot:= 0 else inc(slot);
+        if slot < cMaxSlotIndex then inc(slot);
         HHGear^.MsgParam:= slot;
         ChangeAmmo(HHGear)
         end
@@ -848,10 +848,12 @@
     if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then
         Gear^.dY:= _0;
     Gear^.State:= Gear^.State or gstMoving;
-    if (CurrentHedgehog^.Gear = Gear)
-        and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then
+    if (CurrentHedgehog^.Gear = Gear) and (CurrentHedgehog^.Gear^.State and gstHHDriven <> 0) and
+       (not CurrentTeam^.ExtDriven) and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then
         begin
         // TODO: why so aggressive at setting FollowGear when falling?
+        // because hog was being yanked out of frame by other stuff when doing a complicated jump/chute/saucer/roping.
+        // added a couple more conditions to make it a bit less aggressive, at cost of possibly spectator failing to follow a maneuver
         FollowGear:= Gear;
         end;
     if isUnderwater then
@@ -1273,7 +1275,21 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepHedgehog(Gear: PGear);
+var tX: hwFloat;
 begin
+tX:= Gear^.X;
+if WorldWrap(Gear) then
+    begin
+    if (WorldEdge <> weBounce) and (Gear = CurrentHedgehog^.Gear) and 
+       (CurAmmoGear <> nil) and (CurAmmoGear^.Kind =gtRope) and (CurAmmoGear^.Elasticity <> _0) then
+       CurAmmoGear^.PortalCounter:= 1;
+    if (WorldEdge = weWrap) and (TestCollisionXwithGear(Gear, 1) or TestCollisionXwithGear(Gear, -1))  then
+        begin
+        Gear^.X:= tX;
+        Gear^.dX.isNegative:= (hwRound(tX) > leftX+Gear^.Radius*2)
+        end
+    end;
+
 CheckSum:= CheckSum xor Gear^.Hedgehog^.BotLevel;
 if (Gear^.Message and gmDestroy) <> 0 then
     begin
--- a/hedgewars/uGearsList.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uGearsList.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -281,6 +281,7 @@
                 gear^.RenderTimer:= true;
                 gear^.Elasticity:= _0_9;
                 gear^.Tag:= 0;
+                gear^.State:= Gear^.State or gstSubmersible
                 end;
    gtSeduction: begin
                 gear^.Radius:= 250;
--- a/hedgewars/uGearsRender.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uGearsRender.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -671,6 +671,25 @@
                     end;
                 amGirder: begin
                     DrawSpriteRotated(sprHandConstruction, hx, hy, sign, aangle);
+                    if WorldEdge = weWrap then
+                        begin
+                        if hwRound(Gear^.X) < leftX+256 then
+                            DrawSpriteClipped(sprGirder,
+                                            rightX+(ox-leftX)-256,
+                                            oy-256,
+                                            LongInt(topY)+WorldDy,
+                                            LongInt(rightX)+WorldDx,
+                                            cWaterLine+WorldDy,
+                                            LongInt(leftX)+WorldDx);
+                        if hwRound(Gear^.X) > rightX-256 then
+                            DrawSpriteClipped(sprGirder,
+                                            leftX-(rightX-ox)-256,
+                                            oy-256,
+                                            LongInt(topY)+WorldDy,
+                                            LongInt(rightX)+WorldDx,
+                                            cWaterLine+WorldDy,
+                                            LongInt(leftX)+WorldDx)
+                        end;
                     DrawSpriteClipped(sprGirder,
                                     ox-256,
                                     oy-256,
--- a/hedgewars/uGearsUtils.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uGearsUtils.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -53,13 +53,14 @@
 function  GetAmmo(Hedgehog: PHedgehog): TAmmoType;
 function  GetUtility(Hedgehog: PHedgehog): TAmmoType;
 
+function WorldWrap(var Gear: PGear): boolean;
+
 
 
 function MakeHedgehogsStep(Gear: PGear) : boolean;
 
 var doStepHandlers: array[TGearType] of TGearStepProcedure;
 
-
 implementation
 uses uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc,
     uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore,
@@ -486,7 +487,10 @@
             CurAmmoGear^.Pos := 1000
         end
     else
-        CheckGearDrowning := false;
+        begin
+        if not (Gear^.Kind in [gtJetpack, gtBee]) then Gear^.State:= Gear^.State and not gstSubmersible;  // making it temporary for most gears is more attractive I think
+        CheckGearDrowning := false
+        end
 end;
 
 
@@ -589,6 +593,11 @@
 ignoreNearObjects:= false; // try not skipping proximity at first
 ignoreOverlap:= false; // this not only skips proximity, but allows overlapping objects (barrels, mines, hogs, crates).  Saving it for a 3rd pass.  With this active, winning AI Survival goes back to virtual impossibility
 tryAgain:= true;
+if WorldEdge <> weNone then 
+    begin
+    Left:= max(Left,leftX+Gear^.Radius);
+    Right:= min(Right,rightX-Gear^.Radius)
+    end;
 while tryAgain do
     begin
     delta:= LAND_WIDTH div 16;
@@ -1198,5 +1207,79 @@
 GetUtility:= i
 end;
 
+(*
+Intended to check Gear X/Y against the map left/right edges and apply one of the world modes
+* Normal - infinite world, do nothing
+* Wrap (entering left edge exits at same height on right edge)
+* Bounce (striking edge is treated as a 100% elasticity bounce)
+* From the depths (same as from sky, but from sea, with submersible flag set)
+
+Trying to make the checks a little broader than on first pass to catch things that don't move normally.
+*)
+function WorldWrap(var Gear: PGear): boolean;
+var tdx: hwFloat;
+begin
+WorldWrap:= false;
+if WorldEdge = weNone then exit(false);
+if (hwRound(Gear^.X)-Gear^.Radius < leftX) or
+   (hwRound(Gear^.X)+Gear^.Radius > rightX) then
+    begin
+    if WorldEdge = weWrap then
+        begin
+        if (hwRound(Gear^.X)-Gear^.Radius < leftX) then
+             Gear^.X:= int2hwfloat(rightX-Gear^.Radius)
+        else Gear^.X:= int2hwfloat(leftX+Gear^.Radius);
+        LeftImpactTimer:= 150;
+        RightImpactTimer:= 150
+        end
+    else if WorldEdge = weBounce then
+        begin
+        if (hwRound(Gear^.X)-Gear^.Radius < leftX) then
+            begin
+            LeftImpactTimer:= 333;
+            Gear^.dX.isNegative:= false;
+            Gear^.X:= int2hwfloat(leftX+Gear^.Radius)
+            end
+        else 
+            begin
+            RightImpactTimer:= 333;
+            Gear^.dX.isNegative:= true;
+            Gear^.X:= int2hwfloat(rightX-Gear^.Radius)
+            end;
+        if (Gear^.Radius > 2) and (Gear^.dX.QWordValue > _0_001.QWordValue) then
+            PlaySound(sndMelonImpact)
+        end
+    else if WorldEdge = weSea then
+        begin
+        if (hwRound(Gear^.Y) > cWaterLine) and (Gear^.State and gstSubmersible <> 0) then
+            Gear^.State:= Gear^.State and not gstSubmersible
+        else
+            begin
+            Gear^.State:= Gear^.State or gstSubmersible;
+            Gear^.X:= int2hwFloat(PlayWidth)*int2hwFloat(min(max(0,hwRound(Gear^.Y)),PlayHeight))/PlayHeight;
+            Gear^.Y:= int2hwFloat(cWaterLine+cVisibleWater+Gear^.Radius*2);
+            tdx:= Gear^.dX;
+            Gear^.dX:= -Gear^.dY;
+            Gear^.dY:= tdx;
+            Gear^.dY.isNegative:= true
+            end
+        end;
+(*
+* Window in the sky (Gear moved high into the sky, Y is used to determine X) [unfortunately, not a safe thing to do. shame, I thought aerial bombardment would be kinda neat
+This one would be really easy to freeze game unless it was flagged unfortunately.
+
+    else 
+        begin
+        Gear^.X:= int2hwFloat(PlayWidth)*int2hwFloat(min(max(0,hwRound(Gear^.Y)),PlayHeight))/PlayHeight;
+        Gear^.Y:= -_2048-_256-_256;
+        tdx:= Gear^.dX;
+        Gear^.dX:= Gear^.dY;
+        Gear^.dY:= tdx;
+        Gear^.dY.isNegative:= false
+        end
+*)
+    WorldWrap:= true
+    end;
+end;
 
 end.
--- a/hedgewars/uInputHandler.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uInputHandler.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -40,6 +40,8 @@
 procedure SetBinds(var binds: TBinds);
 procedure SetDefaultBinds;
 procedure chDefaultBind(var id: shortstring);
+procedure loadBinds(cmd, s: shortstring);
+procedure addBind(var binds: TBinds; var id: shortstring);
 
 procedure ControllerInit;
 procedure ControllerAxisEvent(joy, axis: Byte; value: Integer);
@@ -47,7 +49,7 @@
 procedure ControllerButtonEvent(joy, button: Byte; pressed: Boolean);
 
 implementation
-uses uConsole, uCommands, uMisc, uVariables, uConsts, uUtils, uDebug;
+uses uConsole, uCommands, uMisc, uVariables, uConsts, uUtils, uDebug, uPhysFSLayer;
 
 const
     LSHIFT = $0200;
@@ -70,7 +72,6 @@
     //ControllerBalls: array[0..5] of array[0..19] of array[0..1] of Integer;
     //ControllerHats: array[0..5] of array[0..19] of Byte;
     //ControllerButtons: array[0..5] of array[0..19] of Byte;
-    usingDBinds: boolean;
 
 function  KeyNameToCode(name: shortstring): LongInt; inline;
 begin
@@ -314,6 +315,8 @@
 DefaultBinds[KeyNameToCode('j0a1d')]:= '+down';
 for i:= 1 to 10 do DefaultBinds[KeyNameToCode('f'+IntToStr(i))]:= 'slot '+IntToStr(i);
 for i:= 1 to 5  do DefaultBinds[KeyNameToCode(IntToStr(i))]:= 'timer '+IntToStr(i);
+
+loadBinds('dbind', cPathz[ptData] + '/settings.ini');
 end;
 
 procedure SetBinds(var binds: TBinds);
@@ -447,22 +450,68 @@
     ProcessKey(k +  ControllerNumAxes[joy]*2 + ControllerNumHats[joy]*4 + button, pressed);
 end;
 
-// Bind that isn't a team bind, but overrides defaultbinds.
-// When first called, DefaultBinds is cleared, because we assume we are getting a full list of dbinds.
-procedure chDefaultBind(var id: shortstring);
+procedure loadBinds(cmd, s: shortstring);
+var i: LongInt;
+    f: PFSFile;
+    p, l: shortstring;
+    b: byte;
+begin
+    AddFileLog('[BINDS] Loading binds from: ' + s);
+
+    l:= '';
+    if pfsExists(s) then
+        begin
+        f:= pfsOpenRead(s);
+        while (not pfsEOF(f)) and (l <> '[Binds]') do
+            pfsReadLn(f, l);
+
+        while (not pfsEOF(f)) and (l <> '') do
+            begin
+            pfsReadLn(f, l);
+
+            p:= '';
+            i:= 1;
+            while (i <= length(l)) and (l[i] <> '=') do
+                begin
+                if l[i] <> '%' then
+                    begin
+                    p:= p + l[i];
+                    inc(i)
+                    end else
+                    begin
+                    l[i]:= '$';
+                    val(copy(l, i, 3), b);
+                    p:= p + char(b);
+                    inc(i, 3)
+                    end;
+                end;
+
+            if i < length(l) then
+                begin
+                l:= copy(l, i + 1, length(l) - i);
+                if l <> 'default' then
+                    begin
+                    p:= cmd + ' ' + l + ' ' + p;
+                    ParseCommand(p, true)
+                    end
+                end
+            end;
+
+        pfsClose(f)
+        end 
+        else
+            AddFileLog('[BINDS] file not found');
+end;
+
+
+procedure addBind(var binds: TBinds; var id: shortstring);
 var KeyName, Modifier, tmp: shortstring;
-    b: LongInt;
+    i, b: LongInt;
 begin
 KeyName:= '';
 Modifier:= '';
 
-if (not usingDBinds) then
-    begin
-    usingDBinds:= true;
-    FillByte(DefaultBinds, SizeOf(DefaultBinds), 0);
-    end;
-
-if (Pos('mod:', id) <> 0) then
+if(Pos('mod:', id) <> 0)then
     begin
     tmp:= '';
     SplitBySpace(id, tmp);
@@ -479,12 +528,27 @@
 if b = 0 then
     OutError(errmsgUnknownVariable + ' "' + id + '"', false)
 else
-    DefaultBinds[b]:= KeyName;
+    begin 
+    // add bind: first check if this cmd is already bound, and remove old bind
+    i:= cKbdMaxIndex;
+    repeat
+        dec(i)
+    until (i < 0) or (binds[i] = KeyName);
+    if (i >= 0) then
+        binds[i]:= '';
+
+    binds[b]:= KeyName;
+    end
+end;
+
+// Bind that isn't a team bind, but overrides defaultbinds.
+procedure chDefaultBind(var id: shortstring);
+begin
+    addBind(DefaultBinds, id)
 end;
 
 procedure initModule;
 begin
-    usingDBinds:= false;
     RegisterVariable('dbind', @chDefaultBind, true );
 end;
 
--- a/hedgewars/uLand.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uLand.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -729,26 +729,27 @@
     // also try basing cave dimensions on map/template dimensions, if they exist
     for w:= 0 to 5 do // width of 3 allowed hogs to be knocked through with grenade
         begin
-        for y:= topY to LAND_HEIGHT - 1 do
-                begin
-                Land[y, leftX + w]:= lfIndestructible;
-                Land[y, rightX - w]:= lfIndestructible;
-                if (y + w) mod 32 < 16 then
-                    c:= AMask
-                else
-                    c:= AMask or RMask or GMask; // FF00FFFF
+        if (WorldEdge <> weBounce) and (WorldEdge <> weWrap) then
+            for y:= topY to LAND_HEIGHT - 1 do
+                    begin
+                    Land[y, leftX + w]:= lfIndestructible;
+                    Land[y, rightX - w]:= lfIndestructible;
+                    if (y + w) mod 32 < 16 then
+                        c:= AMask
+                    else
+                        c:= AMask or RMask or GMask; // FF00FFFF
 
-                if (cReducedQuality and rqBlurryLand) = 0 then
-                    begin
-                    LandPixels[y, leftX + w]:= c;
-                    LandPixels[y, rightX - w]:= c;
-                    end
-                else
-                    begin
-                    LandPixels[y div 2, (leftX + w) div 2]:= c;
-                    LandPixels[y div 2, (rightX - w) div 2]:= c;
+                    if (cReducedQuality and rqBlurryLand) = 0 then
+                        begin
+                        LandPixels[y, leftX + w]:= c;
+                        LandPixels[y, rightX - w]:= c;
+                        end
+                    else
+                        begin
+                        LandPixels[y div 2, (leftX + w) div 2]:= c;
+                        LandPixels[y div 2, (rightX - w) div 2]:= c;
+                        end;
                     end;
-                end;
 
         for x:= leftX to rightX do
             begin
--- a/hedgewars/uLandPainted.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uLandPainted.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -27,7 +27,7 @@
 procedure freeModule;
 
 implementation
-uses uLandGraphics, uConsts, uVariables, uUtils, SDLh, uCommands, uDebug;
+uses uLandGraphics, uConsts, uVariables, uUtils, SDLh, uCommands, uDebug, uScript;
 
 type PointRec = packed record
     X, Y: SmallInt;
@@ -88,7 +88,11 @@
     radius:= 0;
 
     pe:= pointsListHead;
-    TryDo((pe = nil) or (pe^.point.flags and $80 <> 0), 'Corrupted draw data', true);
+    while (pe <> nil) and (pe^.point.flags and $80 = 0) do
+        begin
+        ScriptCall('onSpecialPoint', pe^.point.X, pe^.point.Y, pe^.point.flags);
+        pe:= pe^.next;
+        end;
 
     while(pe <> nil) do
         begin
@@ -110,7 +114,7 @@
             end;
 
         prevPoint:= pe^.point;
-        pe:= pe^.next;  
+        pe:= pe^.next;
         end;
 end;
 
--- a/hedgewars/uPhysFSLayer.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uPhysFSLayer.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -145,10 +145,12 @@
     AddFileLog('[PhysFS] mount ' + PathPrefix + ': ' + inttostr(i));
     i:= PHYSFS_mount(Str2PChar(UserPathPrefix + '/Data'), nil, false);
     AddFileLog('[PhysFS] mount ' + UserPathPrefix + '/Data: ' + inttostr(i));
-    i:= PHYSFS_mount(Str2PChar(UserPathPrefix + '/Teams'), '/Teams', false);
-    AddFileLog('[PhysFS] mount ' + UserPathPrefix + '/Teams: ' + inttostr(i));
 
     hedgewarsMountPackages;
+
+    i:= PHYSFS_mount(Str2PChar(UserPathPrefix), nil, false);
+    // need access to teams and frontend configs (for bindings)
+    AddFileLog('[PhysFS] mount ' + UserPathPrefix + ': ' + inttostr(i)); 
 end;
 
 procedure freeModule;
--- a/hedgewars/uRender.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uRender.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -44,6 +44,7 @@
 procedure DrawCircle            (X, Y, Radius, Width: LongInt);
 procedure DrawCircle            (X, Y, Radius, Width: LongInt; r, g, b, a: Byte);
 
+procedure DrawLine              (X0, Y0, X1, Y1, Width: Single; color: LongWord); inline;
 procedure DrawLine              (X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte);
 procedure DrawFillRect          (r: TSDL_Rect);
 procedure DrawHedgehog          (X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real);
@@ -348,6 +349,11 @@
     DrawTexture(X - round(Source^.w * scale) div 2, Top, Source, scale)
 end;
 
+procedure DrawLine(X0, Y0, X1, Y1, Width: Single; color: LongWord); inline;
+begin
+DrawLine(X0, Y0, X1, Y1, Width, (color shr 24) and $FF, (color shr 16) and $FF, (color shr 8) and $FF, color and $FF)
+end;
+
 procedure DrawLine(X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte);
 var VertexBuffer: array [0..1] of TVertex2f;
 begin
@@ -533,7 +539,7 @@
 procedure Tint(r, g, b, a: Byte); inline;
 var nc, tw: Longword;
 begin
-    nc:= (a shl 24) or (b shl 16) or (g shl 8) or r;
+    nc:= (r shl 24) or (g shl 16) or (b shl 8) or a;
 
     if nc = lastTint then
         exit;
@@ -554,6 +560,7 @@
 
 procedure Tint(c: Longword); inline;
 begin
+    if c = lastTint then exit;
     Tint(((c shr 24) and $FF), ((c shr 16) and $FF), (c shr 8) and $FF, (c and $FF))
 end;
 
--- a/hedgewars/uScript.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uScript.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -95,6 +95,7 @@
     ScriptAmmoDelay : shortstring;
     ScriptAmmoReinforcement : shortstring;
     ScriptLoaded : boolean;
+    mapDims : boolean;
 
 procedure ScriptPrepareAmmoStore; forward;
 procedure ScriptApplyAmmoStore; forward;
@@ -2065,7 +2066,8 @@
     end;
 
 ScriptSetInteger('ClansCount', ClansCount);
-ScriptSetInteger('TeamsCount', TeamsCount)
+ScriptSetInteger('TeamsCount', TeamsCount);
+mapDims:= false
 end;
 
 
@@ -2088,7 +2090,10 @@
 begin
 s:= cPathz[ptData] + name;
 if not pfsExists(s) then
+    begin
+    AddFileLog('[LUA] Script not found: ' + name);
     exit;
+    end;
 
 f:= pfsOpenRead(s);
 if f = nil then 
@@ -2119,8 +2124,9 @@
 ScriptSetInteger('GameTime', GameTicks);
 ScriptSetInteger('TotalRounds', TotalRounds);
 ScriptSetInteger('WaterLine', cWaterLine);
-if GameTicks = 0 then
+if not mapDims then
     begin
+    mapDims:= true;
     ScriptSetInteger('LAND_WIDTH', LAND_WIDTH);
     ScriptSetInteger('LAND_HEIGHT', LAND_HEIGHT);
     ScriptSetInteger('LeftX', leftX);
@@ -2622,6 +2628,7 @@
 
 procedure initModule;
 begin
+mapDims:= false;
 end;
 
 procedure freeModule;
--- a/hedgewars/uStore.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uStore.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -704,20 +704,20 @@
 end;
 
 procedure SetupOpenGL;
-var name: array[byte] of char;
+var buf: array[byte] of char;
     AuxBufNum: LongInt = 0;
     tmpstr: AnsiString;
     tmpint: LongInt;
     tmpn: LongInt;
 begin
 {$IFDEF SDL2}
-    name:= SDL_GetCurrentVideoDriver();
+    AddFileLog('Setting up OpenGL (using driver: ' + shortstring(SDL_GetCurrentVideoDriver()) + ')');
 {$ELSE}
-    name:= SDL_VideoDriverName(name, sizeof(name));
+    buf[0]:= char(0); // avoid compiler hint
+    AddFileLog('Setting up OpenGL (using driver: ' + shortstring(SDL_VideoDriverName(buf, sizeof(buf))) + ')');
 {$ENDIF}
 
     AuxBufNum:= AuxBufNum;
-    AddFileLog('Setting up OpenGL (using driver: ' + shortstring(name) + ')');
 
 {$IFDEF MOBILE}
     // TODO: this function creates an opengles1.1 context
--- a/hedgewars/uTeams.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uTeams.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -43,7 +43,7 @@
 
 implementation
 uses uLocale, uAmmos, uChat, uVariables, uUtils, uIO, uCaptions, uCommands, uDebug,
-    uGearsUtils, uGearsList, uVisualGearsList, uPhysFSLayer
+    uGearsUtils, uGearsList, uVisualGearsList, uTextures
     {$IFDEF USE_TOUCH_INTERFACE}, uTouch{$ENDIF};
 
 var MaxTeamHealth: LongInt;
@@ -570,57 +570,13 @@
 
 procedure loadTeamBinds(s: shortstring);
 var i: LongInt;
-    f: PFSFile;
-    p, l: shortstring;
-    b: byte;
 begin
-    l:= '';
-    
     for i:= 1 to length(s) do
         if s[i] in ['\', '/', ':'] then s[i]:= '_';
-        
-    s:= cPathz[ptTeams] + '/' + s + '.hwt';
-    if pfsExists(s) then
-        begin
-        AddFileLog('Loading binds from: ' + s);
-        f:= pfsOpenRead(s);
-        while (not pfsEOF(f)) and (l <> '[Binds]') do
-            pfsReadLn(f, l);
-
-        while (not pfsEOF(f)) and (l <> '') do
-            begin
-            pfsReadLn(f, l);
 
-            p:= '';
-            i:= 1;
-            while (i <= length(l)) and (l[i] <> '=') do
-                begin
-                if l[i] <> '%' then
-                    begin
-                    p:= p + l[i];
-                    inc(i)
-                    end else
-                    begin
-                    l[i]:= '$';
-                    val(copy(l, i, 3), b);
-                    p:= p + char(b);
-                    inc(i, 3)
-                    end;
-                end;
+    s:= cPathz[ptTeams] + '/' + s + '.hwt';
 
-            if i < length(l) then
-                begin
-                l:= copy(l, i + 1, length(l) - i);
-                if l <> 'default' then
-                    begin
-                    p:= 'bind ' + l + ' ' + p;
-                    ParseCommand(p, true)
-                    end
-                end
-            end;
-
-        pfsClose(f)
-        end
+    loadBinds('bind', s);
 end;
 
 procedure chAddTeam(var s: shortstring);
@@ -665,43 +621,11 @@
 end;
 
 procedure chBind(var id: shortstring);
-var KeyName, Modifier, tmp: shortstring;
-    i, b: LongInt;
 begin
-KeyName:= '';
-Modifier:= '';
-
-if CurrentTeam = nil then
-    exit;
-
-if(Pos('mod:', id) <> 0)then
-    begin
-    tmp:= '';
-    SplitBySpace(id, tmp);
-    Modifier:= id;
-    id:= tmp;
-    end;
+    if CurrentTeam = nil then
+        exit;
 
-SplitBySpace(id, KeyName);
-if KeyName[1]='"' then
-    Delete(KeyName, 1, 1);
-if KeyName[byte(KeyName[0])]='"' then
-    Delete(KeyName, byte(KeyName[0]), 1);
-b:= KeyNameToCode(id, Modifier);
-if b = 0 then
-    OutError(errmsgUnknownVariable + ' "' + id + '"', false)
-else
-    begin 
-    // add bind: first check if this cmd is already bound, and remove old bind
-    i:= cKbdMaxIndex;
-    repeat
-        dec(i)
-    until (i < 0) or (CurrentTeam^.Binds[i] = KeyName);
-    if (i >= 0) then
-        CurrentTeam^.Binds[i]:= '';
-
-    CurrentTeam^.Binds[b]:= KeyName;
-    end
+    addBind(CurrentTeam^.Binds, id)
 end;
 
 procedure chTeamGone(var s:shortstring);
@@ -790,8 +714,26 @@
     for i:= 0 to Pred(TeamsCount) do
         begin
         for h:= 0 to cMaxHHIndex do
-            if TeamsArray[i]^.Hedgehogs[h].GearHidden <> nil then
-                Dispose(TeamsArray[i]^.Hedgehogs[h].GearHidden);
+            with TeamsArray[i]^.Hedgehogs[h] do
+                begin
+                if GearHidden <> nil then
+                    Dispose(GearHidden);
+                    
+                FreeTexture(NameTagTex);
+                FreeTexture(HealthTagTex);
+                FreeTexture(HatTex);
+                end;
+                
+        with TeamsArray[i]^ do
+            begin
+            FreeTexture(NameTagTex);
+            FreeTexture(CrosshairTex);
+            FreeTexture(GraveTex);
+            FreeTexture(HealthTex);
+            FreeTexture(AIKillsTex);
+            FreeTexture(FlagTex);
+            end;
+        
         Dispose(TeamsArray[i]);
     end;
 for i:= 0 to Pred(ClansCount) do
--- a/hedgewars/uTypes.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uTypes.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -173,6 +173,8 @@
     TRenderMode = (rmDefault, rmLeftEye, rmRightEye);
     TStereoMode = (smNone, smRedCyan, smCyanRed, smRedBlue, smBlueRed, smRedGreen, smGreenRed, smHorizontal, smVertical);
 
+    TWorldEdge = (weNone, weWrap, weBounce, weSea, weSky);
+
     THHFont = record
             Handle: PTTF_Font;
             Height: LongInt;
--- a/hedgewars/uVariables.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uVariables.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -82,6 +82,9 @@
     GameType        : TGameType;
     InputMask       : LongWord;
     GameFlags       : Longword;
+    WorldEdge       : TWorldEdge;
+    LeftImpactTimer : LongWord;
+    RightImpactTimer: LongWord;
     TurnTimeLeft    : Longword;
     TagTurnTimeLeft : Longword;
     ReadyTimeLeft   : Longword;
@@ -841,7 +844,8 @@
             Probability: 0;
             NumberInCase: 1;
             Ammo: (Propz: ammoprop_ForwMsgs or
-                          ammoprop_NeedUpDown;
+                          ammoprop_NeedUpDown or
+                          ammoprop_DoesntStopTimerInMultiShoot;
                 Count: AMMO_INFINITE;
                 NumPerTurn: 1;
                 Timer: 0;
@@ -922,7 +926,8 @@
                           ammoprop_AttackInMove or
                           ammoprop_Utility or
                           ammoprop_AltAttack or
-                          ammoprop_NeedUpDown;
+                          ammoprop_NeedUpDown or
+                          ammoprop_DoesntStopTimerWhileAttacking;
                     Count: 5;
                     NumPerTurn: 0;
                     Timer: 0;
@@ -974,7 +979,7 @@
             NameTex: nil;
             Probability: 20;
             NumberInCase: 2;
-            Ammo: (Propz: ammoprop_NeedUpDown;
+            Ammo: (Propz: ammoprop_NeedUpDown or ammoprop_DoesntStopTimerInMultiShoot;
                 Count: 3;
                 NumPerTurn: 3;
                 Timer: 0;
@@ -1736,9 +1741,10 @@
             NameTex: nil;
             Probability: 20;
             NumberInCase: 2;
-            Ammo: (Propz: ammoprop_NeedUpDown or 
+            Ammo: (Propz: ammoprop_NeedUpDown or
                     ammoprop_OscAim or
-                    ammoprop_NoMoveAfter;
+                    ammoprop_NoMoveAfter or
+                    ammoprop_DoesntStopTimerInMultiShoot;
                 Count: 2;
                 NumPerTurn: 1;
                 Timer: 0;
@@ -2446,6 +2452,9 @@
 
     InputMask           := $FFFFFFFF;
     GameFlags           := 0;
+    WorldEdge           := weNone;
+    LeftImpactTimer     := 0;
+    RightImpactTimer    := 0;
     TurnTimeLeft        := 0;
     TagTurnTimeLeft     := 0;
     cSuddenDTurns       := 15;
--- a/hedgewars/uVideoRec.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uVideoRec.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -367,6 +367,10 @@
 
 procedure initModule;
 begin
+    // we need to make sure these variables are initialized before the main loop
+    // or the wrapper will keep the default values of preinit
+    cScreenWidth:= max(cWindowedWidth, 640);
+    cScreenHeight:= max(cWindowedHeight, 480);
 end;
 
 procedure freeModule;
--- a/hedgewars/uWorld.pas	Wed Sep 25 05:42:16 2013 +0300
+++ b/hedgewars/uWorld.pas	Mon Oct 28 14:07:04 2013 +0100
@@ -85,6 +85,7 @@
     AmmoMenuTex     : PTexture;
     HorizontOffset: LongInt;
     cOffsetY: LongInt;
+    WorldEnd, WorldFade : array[0..3] of HwColor4f;
 
 const cStereo_Sky           = 0.0500;
       cStereo_Horizon       = 0.0250;
@@ -1129,6 +1130,8 @@
     highlight: Boolean;
     smallScreenOffset, offsetX, offsetY, screenBottom: LongInt;
     VertexBuffer: array [0..3] of TVertex2f;
+    lw, lh: GLfloat;
+    c1, c2: LongWord; // couple of colours for edges
 begin
 if (cReducedQuality and rqNoBackground) = 0 then
     begin
@@ -1235,6 +1238,106 @@
     end;
 {$WARNINGS ON}
 
+if WorldEdge <> weNone then
+    begin
+(* I think for a bounded world, will fill the left and right areas with black or something. Also will probably want various border effects/animations based on border type.  Prob also, say, trigger a border animation timer on an impact. *)
+
+    glDisable(GL_TEXTURE_2D);
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    glEnableClientState(GL_COLOR_ARRAY);
+
+    glPushMatrix;
+    glTranslatef(WorldDx, WorldDy, 0);
+    glColorPointer(4, GL_UNSIGNED_BYTE, 0, @WorldFade[0]);
+
+    VertexBuffer[0].X:= leftX-20;
+    VertexBuffer[0].Y:= -3000;
+    VertexBuffer[1].X:= leftX-20;
+    VertexBuffer[1].Y:= cWaterLine+cVisibleWater;
+    VertexBuffer[2].X:= leftX+30;
+    VertexBuffer[2].Y:= cWaterLine+cVisibleWater;
+    VertexBuffer[3].X:= leftX+30;
+    VertexBuffer[3].Y:= -3000;
+
+    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
+
+    VertexBuffer[0].X:= rightX+20;
+    VertexBuffer[1].X:= rightX+20;
+    VertexBuffer[2].X:= rightX-30;
+    VertexBuffer[3].X:= rightX-30;
+
+    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
+
+    glColorPointer(4, GL_UNSIGNED_BYTE, 0, @WorldEnd[0]);
+
+    VertexBuffer[0].X:= -5000;
+    VertexBuffer[1].X:= -5000;
+    VertexBuffer[2].X:= leftX-20;
+    VertexBuffer[3].X:= leftX-20;
+
+    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
+
+    VertexBuffer[0].X:= rightX+5000;
+    VertexBuffer[1].X:= rightX+5000;
+    VertexBuffer[2].X:= rightX+20;
+    VertexBuffer[3].X:= rightX+20;
+
+    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
+
+    glPopMatrix;
+    glDisableClientState(GL_COLOR_ARRAY);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+    glColor4ub($FF, $FF, $FF, $FF); // must not be Tint() as color array seems to stay active and color reset is required
+    glEnable(GL_TEXTURE_2D);
+
+    // I'd still like to have things happen to the border when a wrap or bounce just occurred, based on a timer 
+    if WorldEdge = weBounce then
+        begin
+        // could maybe alternate order of these on a bounce, or maybe drop the outer ones.
+        if LeftImpactTimer mod 2 = 0 then
+            begin
+            c1:= $5454FFFF; c2:= $FFFFFFFF;
+            end
+        else begin
+            c1:= $FFFFFFFF; c2:= $5454FFFF;
+            end;
+        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 7.0,   c1);
+        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 5.0,   c2);
+        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 3.0,   c1);
+        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 1.0,   c2);
+        if RightImpactTimer mod 2 = 0 then
+            begin
+            c1:= $5454FFFF; c2:= $FFFFFFFF;
+            end
+        else begin
+            c1:= $FFFFFFFF; c2:= $5454FFFF;
+            end;
+        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 7.0, c1);
+        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 5.0, c2);
+        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 3.0, c1);
+        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 1.0, c2)
+        end
+    else if WorldEdge = weWrap then
+        begin
+        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 5.0, $A0, $30, $60, max(50,255-LeftImpactTimer));
+        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 2.0, $FF0000FF);
+        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 5.0, $A0, $30, $60, max(50,255-RightImpactTimer));
+        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 2.0, $FF0000FF);
+        end
+    else
+        begin
+        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 5.0, $2E8B5780);
+        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 5.0, $2E8B5780)
+        end;
+    if LeftImpactTimer > Lag then dec(LeftImpactTimer,Lag) else LeftImpactTimer:= 0;
+    if RightImpactTimer > Lag then dec(RightImpactTimer,Lag) else RightImpactTimer:= 0
+    end;
+
 // this scale is used to keep the various widgets at the same dimension at all zoom levels
 SetScale(cDefaultZoomLevel);
 
@@ -1631,7 +1734,7 @@
         DrawSprite(sprArrow, TargetCursorPoint.X, cScreenHeight - TargetCursorPoint.Y, (RealTicks shr 6) mod 8)
         end
     end;
-isFirstFrame:= false
+isFirstFrame:= false;
 end;
 
 var PrevSentPointTime: LongWord = 0;
@@ -1899,6 +2002,16 @@
     AMState:= AMHidden;
     isFirstFrame:= true;
     stereoDepth:= stereoDepth; // avoid hint
+
+    FillChar(WorldFade, sizeof(WorldFade), 0);
+    WorldFade[0].a:= 255;
+    WorldFade[1].a:= 255;
+    FillChar(WorldEnd, sizeof(WorldEnd), 0);
+    WorldEnd[0].a:= 255;
+    WorldEnd[1].a:= 255;
+    WorldEnd[2].a:= 255;
+    WorldEnd[3].a:= 255;
+
 end;
 
 procedure freeModule;
--- a/project_files/hedgewars.pro	Wed Sep 25 05:42:16 2013 +0300
+++ b/project_files/hedgewars.pro	Mon Oct 28 14:07:04 2013 +0100
@@ -87,7 +87,6 @@
     ../QTfrontend/achievements.h \
     ../QTfrontend/binds.h \
     ../QTfrontend/ui_hwform.h \
-    ../QTfrontend/KB.h \
     ../QTfrontend/hwconsts.h \
     ../QTfrontend/sdlkeys.h \
     ../QTfrontend/ui/mouseoverfilter.h \
--- a/share/hedgewars/Data/Scripts/Multiplayer/Racer.lua	Wed Sep 25 05:42:16 2013 +0300
+++ b/share/hedgewars/Data/Scripts/Multiplayer/Racer.lua	Mon Oct 28 14:07:04 2013 +0100
@@ -92,6 +92,10 @@
 local currY = {}
 local currCount = 0
 
+local specialPointsX = {}
+local specialPointsY = {}
+local specialPointsCount = 0
+
 --------------------------
 -- hog and team tracking variales
 --------------------------
@@ -489,6 +493,10 @@
         lastRound = TotalRounds
         RoundHasChanged = false -- true
 
+        for i = 0, (specialPointsCount-1) do
+                PlaceWayPoint(specialPointsX[i], specialPointsY[i])
+        end
+
         RebuildTeamInfo()
 
         ShowMission     (
@@ -501,7 +509,7 @@
                                 "", 4, 4000
                                 )
 
-	TryRepositionHogs()
+        TryRepositionHogs()
 
 end
 
@@ -524,10 +532,16 @@
 
 end
 
+function onSpecialPoint(x,y,flag)
+    specialPointsX[specialPointsCount] = x
+    specialPointsY[specialPointsCount] = y
+    specialPointsCount = specialPointsCount + 1
+end
+
 function onNewTurn()
 
         CheckForNewRound()
-	TryRepositionHogs()
+        TryRepositionHogs()
 
         racerActive = false