update with default webgl
authorkoda
Mon, 17 Jun 2013 16:56:58 +0200
branchwebgl
changeset 9197 e4e366013e9a
parent 9168 20ff80421736 (current diff)
parent 9195 e653e96b0ec3 (diff)
child 9199 9ed29795d2a3
update with default
CMakeLists.txt
QTfrontend/game.cpp
QTfrontend/hwform.cpp
QTfrontend/hwform.h
QTfrontend/ui/page/pagevideos.cpp
gameServer/Actions.hs
hedgewars/uGearsList.pas
hedgewars/uIO.pas
hedgewars/uScript.pas
hedgewars/uStats.pas
hedgewars/uTypes.pas
misc/libphysfs/CMakeLists.txt
--- a/CMakeLists.txt	Sun Jun 16 00:46:11 2013 +0400
+++ b/CMakeLists.txt	Mon Jun 17 16:56:58 2013 +0200
@@ -2,7 +2,7 @@
 
 #initialise cmake environment
 cmake_minimum_required(VERSION 2.6.0)
-foreach(hwpolicy CMP0003 CMP0012 CMP0017)
+foreach(hwpolicy CMP0003 CMP0012 CMP0017 CMP0018)
     if(POLICY ${hwpolicy})
         cmake_policy(SET ${hwpolicy} NEW)
     endif()
--- a/QTfrontend/HWApplication.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/HWApplication.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -23,11 +23,11 @@
 #include "hwform.h"
 #include "MessageDialog.h"
 
-#if !defined(Q_WS_WIN)
+#if !defined(Q_OS_WIN)
 #include "signal.h"
 #endif
 
-#if !defined(Q_WS_WIN)
+#if !defined(Q_OS_WIN)
 void terminateFrontend(int signal)
 {
     Q_UNUSED(signal);
@@ -38,7 +38,7 @@
 HWApplication::HWApplication(int &argc, char **argv) :
     QApplication(argc, argv)
 {
-#if !defined(Q_WS_WIN)
+#if !defined(Q_OS_WIN)
     signal(SIGINT, &terminateFrontend);
 #endif
 #if 0
--- a/QTfrontend/campaign.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/campaign.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -50,3 +50,14 @@
     campfile.setIniCodec("UTF-8");
     return campfile.value(QString("Mission %1/Script").arg(mNum)).toString();
 }
+
+QString getCampaignImage(QString campaign, unsigned int mNum)
+{
+	return getCampaignScript(campaign,mNum).replace(QString(".lua"),QString(".png"));
+}
+
+QString getCampaignMissionName(QString campaign, unsigned int mNum) 
+{
+	return getCampaignScript(campaign,mNum).replace(QString(".lua"),QString(""));
+}
+
--- a/QTfrontend/campaign.h	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/campaign.h	Mon Jun 17 16:56:58 2013 +0200
@@ -25,5 +25,7 @@
 QStringList getCampMissionList(QString & campaign);
 unsigned int getCampProgress(QString & teamName, QString & campName);
 QString getCampaignScript(QString campaign, unsigned int mNum);
+QString getCampaignImage(QString campaign, unsigned int mNum);
+QString getCampaignMissionName(QString campaign, unsigned int mNum);
 
 #endif
--- a/QTfrontend/game.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/game.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -153,7 +153,7 @@
 
     HWProto::addStringToBuffer(teamscfg, "TL");
     HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
-                               .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount()).data().toString() : "steel"));
+                               .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount()).data(ThemeModel::ActualNameRole).toString() : "steel"));
     HWProto::addStringToBuffer(teamscfg, "eseed " + QUuid::createUuid().toString());
 
     HWProto::addStringToBuffer(teamscfg, "e$template_filter 2");
--- a/QTfrontend/hedgewars.qrc	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/hedgewars.qrc	Mon Jun 17 16:56:58 2013 +0200
@@ -28,6 +28,16 @@
         <file>res/botlevels/net3.png</file>
         <file>res/botlevels/net4.png</file>
         <file>res/botlevels/net5.png</file>
+        <file>res/campaign/A Classic Fairytale/first_blood.png</file>
+        <file>res/campaign/A Classic Fairytale/shadow.png</file>
+        <file>res/campaign/A Classic Fairytale/journey.png</file>
+        <file>res/campaign/A Classic Fairytale/united.png</file>
+        <file>res/campaign/A Classic Fairytale/backstab.png</file>
+        <file>res/campaign/A Classic Fairytale/dragon.png</file>
+        <file>res/campaign/A Classic Fairytale/family.png</file>
+        <file>res/campaign/A Classic Fairytale/queen.png</file>
+        <file>res/campaign/A Classic Fairytale/enemy.png</file>
+        <file>res/campaign/A Classic Fairytale/epil.png</file>
         <file>res/bonus.png</file>
         <file>res/Hedgehog.png</file>
         <file>res/net.png</file>
@@ -133,6 +143,7 @@
         <file>res/StatsMostSelfDamage.png</file>
         <file>res/StatsSelfKilled.png</file>
         <file>res/StatsSkipped.png</file>
+        <file>res/StatsCustomAchievement.png</file>
         <file>res/Start.png</file>
         <file>res/mapRandom.png</file>
         <file>res/mapMaze.png</file>
--- a/QTfrontend/hwform.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/hwform.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -102,7 +102,7 @@
 #include "DataManager.h"
 #include "AutoUpdater.h"
 
-#ifdef Q_WS_WIN
+#ifdef Q_OS_WIN
 #define WINVER 0x0500
 #include <windows.h>
 #else
@@ -110,7 +110,7 @@
 #include <sys/types.h>
 #endif
 
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
 #include <sys/sysctl.h>
 #endif
 
@@ -194,6 +194,8 @@
     //connect (updateData, SIGNAL(activated()), &DataManager::instance(), SLOT(reload()));
 #endif
 
+	previousCampaignName = "";
+	previousTeamName = "";
     UpdateTeamsLists();
     InitCampaignPage();
     UpdateCampaignPage(0);
@@ -306,7 +308,7 @@
     connect(ui.pageCampaign->BtnStartCampaign, SIGNAL(clicked()), this, SLOT(StartCampaign()));
     connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPage(int)));
     connect(ui.pageCampaign->CBCampaign, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPage(int)));
-
+    connect(ui.pageCampaign->CBMission, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPageMission(int)));
 
     connect(ui.pageSelectWeapon->BtnDelete, SIGNAL(clicked()),
             ui.pageSelectWeapon->pWeapons, SLOT(deleteWeaponsName())); // executed first
@@ -1892,7 +1894,7 @@
 void HWForm::UpdateCampaignPage(int index)
 {
     Q_UNUSED(index);
-
+    
     HWTeam team(ui.pageCampaign->CBTeam->currentText());
     ui.pageCampaign->CBMission->clear();
 
@@ -1901,11 +1903,73 @@
     QString tName = team.name();
     unsigned int n = missionEntries.count();
     unsigned int m = getCampProgress(tName, campaignName);
+    
+    // if the campaign name changes update the campaignMissionDescriptions list
+    // this will be used later in UpdateCampaignPageMission() to update
+    // the mission description in the campaign page
+    bool updateMissionList = false;
+    QSettings * m_info;
+    if(previousCampaignName.compare(campaignName)!=0 || 
+			previousTeamName.compare(tName) != 0) 
+    {
+		if (previousTeamName.compare(tName) != 0 && 
+				previousTeamName.compare("") != 0)
+			index = qMin(m + 1, n);
+		previousCampaignName = campaignName;
+		previousTeamName = tName;
+		updateMissionList = true;
+		// the following code was based on pagetraining.cpp
+		DataManager & dataMgr = DataManager::instance();    
+		// get locale
+		QSettings settings(dataMgr.settingsFileName(),
+						   QSettings::IniFormat);
+		QString loc = settings.value("misc/locale", "").toString();		
+		if (loc.isEmpty())
+			loc = QLocale::system().name();
+		QString campaignDescFile = QString("physfs://Locale/campaigns_" + loc + ".txt");
+		// if file is non-existant try with language only
+		if (!QFile::exists(campaignDescFile))
+			campaignDescFile = QString("physfs://Locale/campaigns_" + loc.remove(QRegExp("_.*$")) + ".txt");
 
+		// fallback if file for current locale is non-existant
+		if (!QFile::exists(campaignDescFile))
+			campaignDescFile = QString("physfs://Locale/campaigns_en.txt");
+		  
+		m_info = new QSettings(campaignDescFile, QSettings::IniFormat, this);
+		m_info->setIniCodec("UTF-8");
+		campaignMissionDescriptions.clear();
+		ui.pageCampaign->CBMission->clear();
+	}
+	
     for (unsigned int i = qMin(m + 1, n); i > 0; i--)
     {
-        ui.pageCampaign->CBMission->addItem(QString("Mission %1: ").arg(i) + QString(missionEntries[i-1]), QString(missionEntries[i-1]));
+        if(updateMissionList) 
+        {
+			campaignMissionDescriptions += m_info->value(campaignName+"-"+ getCampaignMissionName(campaignName,i) + ".desc",
+                                            tr("No description available")).toString();
+		} 
+        ui.pageCampaign->CBMission->addItem(QString("Mission %1: ").arg(i) + QString(missionEntries[i-1]), QString(missionEntries[i-1]));       
     }
+    if(updateMissionList)
+		delete m_info;
+
+    UpdateCampaignPageMission(index);
+}
+
+void HWForm::UpdateCampaignPageMission(int index) 
+{    
+    // update thumbnail
+    QString campaignName = ui.pageCampaign->CBCampaign->currentText();
+    unsigned int mNum = ui.pageCampaign->CBMission->count() - ui.pageCampaign->CBMission->currentIndex();
+    QString image = getCampaignImage(campaignName,mNum);
+    ui.pageCampaign->btnPreview->setIcon(QIcon((":/res/campaign/"+campaignName+"/"+image)));
+    // update description
+    // when campaign changes the UpdateCampaignPageMission is triggered with wrong values
+    // this will cause segfault. This check prevents illegal memory reads
+    if(index > -1 && index < campaignMissionDescriptions.count()) {
+		ui.pageCampaign->lbltitle->setText("<h2>"+ui.pageCampaign->CBMission->currentText()+"</h2>");
+		ui.pageCampaign->lbldescription->setText(campaignMissionDescriptions[index]);
+	}
 }
 
 void HWForm::UpdateCampaignPageProgress(int index)
@@ -1923,7 +1987,7 @@
 
     QString prefix = "\"" + datadir->absolutePath() + "\"";
     QString userPrefix = "\"" + cfgdir->absolutePath() + "\"";
-#ifdef Q_WS_WIN
+#ifdef Q_OS_WIN
     prefix = prefix.replace("/","\\");
     userPrefix = userPrefix.replace("/","\\");
 #endif
--- a/QTfrontend/hwform.h	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/hwform.h	Mon Jun 17 16:56:58 2013 +0200
@@ -127,6 +127,7 @@
         void Music(bool checked);
         void UpdateCampaignPage(int index);
         void UpdateCampaignPageProgress(int index);
+        void UpdateCampaignPageMission(int index);
         void InitCampaignPage();
         void showFeedbackDialog();
         void showFeedbackDialogNetChecked();
@@ -192,6 +193,9 @@
         HWNamegen * namegen;
         AmmoSchemeModel * ammoSchemeModel;
         QStack<int> PagesStack;
+        QString previousCampaignName;
+        QString previousTeamName;
+        QStringList campaignMissionDescriptions;
         QTime eggTimer;
         BGWidget * wBackground;
         QSignalMapper * pageSwitchMapper;
--- a/QTfrontend/main.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/main.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -135,7 +135,7 @@
     HWApplication app(argc, argv);
 
     QLabel *splash = NULL;
-#if defined Q_WS_WIN
+#if defined Q_OS_WIN
     QPixmap pixmap(":res/splash.png");
     splash = new QLabel(0, Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
     splash->setAttribute(Qt::WA_TranslucentBackground);
Binary file QTfrontend/res/StatsCustomAchievement.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/backstab.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/dragon.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/enemy.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/epil.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/family.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/first_blood.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/journey.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/queen.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/shadow.png has changed
Binary file QTfrontend/res/campaign/A Classic Fairytale/united.png has changed
--- a/QTfrontend/ui/page/pagecampaign.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/page/pagecampaign.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -19,6 +19,7 @@
 #include <QGridLayout>
 #include <QPushButton>
 #include <QComboBox>
+#include <QLabel>
 
 #include "pagecampaign.h"
 
@@ -30,19 +31,44 @@
     pageLayout->setColumnStretch(2, 1);
     pageLayout->setRowStretch(0, 1);
     pageLayout->setRowStretch(3, 1);
+    
+    QGridLayout * infoLayout = new QGridLayout();
+    infoLayout->setColumnStretch(0, 1);
+    infoLayout->setColumnStretch(1, 1);
+    infoLayout->setColumnStretch(2, 1);
+    infoLayout->setColumnStretch(3, 1);
+    infoLayout->setColumnStretch(4, 1);
+    infoLayout->setRowStretch(0, 1);
+    infoLayout->setRowStretch(1, 1);
+    
+    // set this as default image first time page is created, this will change in hwform.cpp
+    btnPreview = formattedButton(":/res/campaign/A Classic Fairytale/first_blood.png", true);
+	infoLayout->setAlignment(btnPreview, Qt::AlignHCenter | Qt::AlignVCenter);
+    
+    lbldescription = new QLabel();
+    lbldescription->setAlignment(Qt::AlignHCenter| Qt::AlignTop);
+    lbldescription->setWordWrap(true);
+    
+    lbltitle = new QLabel();
+    lbltitle->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
 
     CBTeam = new QComboBox(this);
     CBMission = new QComboBox(this);
     CBCampaign = new QComboBox(this);
-
-    pageLayout->addWidget(CBTeam, 1, 1);
-    pageLayout->addWidget(CBCampaign, 2, 1);
-    pageLayout->addWidget(CBMission, 3, 1);
+    
+	infoLayout->addWidget(btnPreview,0,1,2,1);
+	infoLayout->addWidget(lbltitle,0,2,1,2);
+	infoLayout->addWidget(lbldescription,1,2,1,2);
+	
+	pageLayout->addLayout(infoLayout, 0, 0, 2, 3);
+    pageLayout->addWidget(CBTeam, 2, 1);
+    pageLayout->addWidget(CBCampaign, 3, 1);
+    pageLayout->addWidget(CBMission, 4, 1);
 
     BtnStartCampaign = new QPushButton(this);
     BtnStartCampaign->setFont(*font14);
     BtnStartCampaign->setText(QPushButton::tr("Go!"));
-    pageLayout->addWidget(BtnStartCampaign, 2, 2);
+    pageLayout->addWidget(BtnStartCampaign, 3, 2);
 
     return pageLayout;
 }
--- a/QTfrontend/ui/page/pagecampaign.h	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/page/pagecampaign.h	Mon Jun 17 16:56:58 2013 +0200
@@ -28,7 +28,10 @@
     public:
         PageCampaign(QWidget* parent = 0);
 
+		QPushButton *btnPreview;
         QPushButton *BtnStartCampaign;
+        QLabel *lbldescription;
+        QLabel *lbltitle;
         QComboBox   *CBMission;
         QComboBox   *CBCampaign;
         QComboBox   *CBTeam;
--- a/QTfrontend/ui/page/pagegamestats.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/page/pagegamestats.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -40,10 +40,15 @@
 
 QLayout * PageGameStats::bodyLayoutDefinition()
 {
+	kindOfPoints = QString("");
+	defaultGraphTitle = true;	
     QGridLayout * pageLayout = new QGridLayout();
     pageLayout->setSpacing(20);
     pageLayout->setColumnStretch(0, 1);
     pageLayout->setColumnStretch(1, 1);
+    pageLayout->setRowStretch(0, 1);    
+    pageLayout->setRowStretch(1, 20);
+    //pageLayout->setRowStretch(1, -1); this should work but there is unnecessary empty space betwin lines if used
     pageLayout->setContentsMargins(7, 7, 7, 0);
 
     QGroupBox * gb = new QGroupBox(this);
@@ -61,15 +66,15 @@
     gbl->addWidget(l);
     gbl->addWidget(labelGameStats);
     gb->setLayout(gbl);
-    pageLayout->addWidget(gb, 1, 1, 1, 2);
+    pageLayout->addWidget(gb, 1, 1);
 
     // graph
     graphic = new FitGraphicsView(gb);
-    l = new QLabel(this);
-    l->setTextFormat(Qt::RichText);
-    l->setText("<br><h1><img src=\":/res/StatsH.png\"> " + PageGameStats::tr("Health graph") + "</h1>");
-    l->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-    gbl->addWidget(l);
+    labelGraphTitle = new QLabel(this);
+    labelGraphTitle->setTextFormat(Qt::RichText);
+    labelGraphTitle->setText("<br><h1><img src=\":/res/StatsH.png\"> " + PageGameStats::tr("Health graph") + "</h1>");
+    labelGraphTitle->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    gbl->addWidget(labelGraphTitle);
     gbl->addWidget(graphic);
     graphic->scale(1.0, -1.0);
     graphic->setBackgroundBrush(QBrush(Qt::black));
@@ -154,28 +159,41 @@
 
 void PageGameStats::renderStats()
 {
-    QGraphicsScene * scene = new QGraphicsScene();
-
-    QMap<quint32, QVector<quint32> >::const_iterator i = healthPoints.constBegin();
-    while (i != healthPoints.constEnd())
-    {
-        quint32 c = i.key();
-        //QColor clanColor = QColor(qRgb((c >> 16) & 255, (c >> 8) & 255, c & 255));
-        QVector<quint32> hps = i.value();
+	graphic->show();
+	labelGraphTitle-> show();
+	if(defaultGraphTitle) {
+		labelGraphTitle->setText("<br><h1><img src=\":/res/StatsH.png\"> " + PageGameStats::tr("Health graph") + "</h1>");
+	} else {
+		defaultGraphTitle = true;
+	}
+	// if not health data sent
+	if(healthPoints.size() == 0) {
+		labelGraphTitle->hide();
+		graphic->hide();
+	} else {
+		QGraphicsScene * scene = new QGraphicsScene();
 
-        QPainterPath path;
-        if (hps.size())
-            path.moveTo(0, hps[0]);
+		QMap<quint32, QVector<quint32> >::const_iterator i = healthPoints.constBegin();
+		while (i != healthPoints.constEnd())
+		{
+			quint32 c = i.key();
+			//QColor clanColor = QColor(qRgb((c >> 16) & 255, (c >> 8) & 255, c & 255));
+			QVector<quint32> hps = i.value();
 
-        for(int t = 1; t < hps.size(); ++t)
-            path.lineTo(t, hps[t]);
+			QPainterPath path;
+			if (hps.size())
+				path.moveTo(0, hps[0]);
 
-        scene->addPath(path, QPen(c));
-        ++i;
-    }
+			for(int t = 1; t < hps.size(); ++t)
+				path.lineTo(t, hps[t]);
 
-    graphic->setScene(scene);
-    graphic->fitInView(graphic->sceneRect());
+			scene->addPath(path, QPen(c));
+			++i;
+		}
+
+		graphic->setScene(scene);
+		graphic->fitInView(graphic->sceneRect());
+	}
 }
 
 void PageGameStats::GameStats(char type, const QString & info)
@@ -217,6 +235,13 @@
             healthPoints[clan].append(hp);
             break;
         }
+        case 'g' :
+        {
+			// TODO: change default picture or add change pic capability
+			defaultGraphTitle = false;
+			labelGraphTitle->setText("<br><h1><img src=\":/res/StatsR.png\"> " + info + "</h1>");
+            break;
+        }
         case 'T':   // local team stats
         {
             //AddStatText("<p>local team: " + info + "</p>");
@@ -232,7 +257,11 @@
             }
             break;
         }
-
+		case 'p' :
+        {
+            kindOfPoints = info;
+            break;
+        }
         case 'P' :
         {
             int i = info.indexOf(' ');
@@ -269,7 +298,13 @@
             }
 
             QString message;
-            QString killstring = PageGameStats::tr("(%1 kill)", "", kills).arg(kills);
+            QString killstring;
+            if(kindOfPoints.compare("") == 0) {
+				killstring = PageGameStats::tr("(%1 kill)", "", kills).arg(kills);
+			} else {
+				killstring = PageGameStats::tr("(%1 %2)", "", kills).arg(kills).arg(kindOfPoints);
+				kindOfPoints = QString("");
+			}
 
             message = QString("<p><h2>%1 %2. <font color=\"%4\">%3</font> ").arg(image, QString::number(playerPosition), playername, clanColor.name()) + killstring + "</h2></p>";
 
@@ -300,6 +335,12 @@
             AddStatText(message);
             break;
         }
+        case 'c' :
+        {
+            QString message = "<p><img src=\":/res/StatsCustomAchievement.png\"> "+info+" </p>";
+            AddStatText(message);
+            break;
+        }
 
     }
 }
--- a/QTfrontend/ui/page/pagegamestats.h	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/page/pagegamestats.h	Mon Jun 17 16:56:58 2013 +0200
@@ -49,6 +49,8 @@
         QLabel *labelGameStats;
         QLabel *labelGameWin;
         QLabel *labelGameRank;
+        QLabel *labelGraphTitle;
+        QString kindOfPoints;
         FitGraphicsView * graphic;
 
     public slots:
@@ -67,6 +69,7 @@
         QMap<quint32, QVector<quint32> > healthPoints;
         unsigned int playerPosition;
         quint32 lastColor;
+        bool defaultGraphTitle;
 
     protected:
         QLayout * bodyLayoutDefinition();
--- a/QTfrontend/ui/page/pagenet.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/page/pagenet.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -73,7 +73,7 @@
     BtnNetSvrStart = formattedButton(QPushButton::tr("Start server"));
     BtnNetSvrStart->setMinimumWidth(180);
     QString serverPath = bindir->absolutePath() + "/hedgewars-server";
-#ifdef Q_WS_WIN
+#ifdef Q_OS_WIN
     serverPath += + ".exe";
 #endif
     QFile server(serverPath);
--- a/QTfrontend/ui/page/pagevideos.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/page/pagevideos.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -414,7 +414,7 @@
             setName(item, newName);
         }
     }
-#ifdef Q_WS_WIN
+#ifdef Q_OS_WIN
     // there is a bug in qt, QDir::rename() doesn't fail on such names but damages files
     if (newName.contains(QRegExp("[\"*:<>?\\/|]")))
     {
--- a/QTfrontend/ui/widget/feedbackdialog.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/widget/feedbackdialog.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -34,7 +34,7 @@
 
 #include <string>
 
-#ifdef Q_WS_WIN
+#ifdef Q_OS_WIN
 #define WINVER 0x0500
 #include <windows.h>
 #else
@@ -42,7 +42,7 @@
 #include <sys/types.h>
 #endif
 
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
 #include <sys/sysctl.h>
 #ifndef _SC_NPROCESSORS_ONLN
 #define _SC_NPROCESSORS_ONLN 58
@@ -207,7 +207,7 @@
     QString processor_name = "Processor: ";
 
     // platform specific code
-#ifdef Q_WS_MACX
+#ifdef Q_OS_MACX
     number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
 
     uint64_t memsize;
@@ -240,7 +240,7 @@
         default: os_version += "\"Unknown version\"\n"; break;
     }
 #endif
-#ifdef Q_WS_WIN
+#ifdef Q_OS_WIN
     SYSTEM_INFO sysinfo;
     GetSystemInfo(&sysinfo);
     number_of_cores += QString::number(sysinfo.dwNumberOfProcessors) + "\n";
@@ -262,7 +262,7 @@
     }
     kernel_line += "Windows kernel\n";
 #endif
-#ifdef Q_WS_X11
+#ifdef Q_OS_X11
     number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
     long pages = sysconf(_SC_PHYS_PAGES),
 /*
@@ -277,7 +277,7 @@
 #endif
 
     // uname -a
-#if defined(Q_WS_X11) || defined(Q_WS_MACX)
+#if defined(Q_OS_X11) || defined(Q_OS_MAC)
     QProcess *process = new QProcess();
     QStringList arguments = QStringList("-a");
     process->start("uname", arguments);
@@ -286,7 +286,7 @@
     delete process;
 #endif
 
-#if (!defined(Q_WS_MACX) && defined(__i386__)) || defined(__x86_64__)
+#if (!defined(Q_OS_MAC) && defined(__i386__)) || defined(__x86_64__)
     // cpu info
     quint32 registers[4];
     quint32 i;
--- a/QTfrontend/ui/widget/roomnameprompt.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/widget/roomnameprompt.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -62,7 +62,7 @@
     QPushButton * btnOkay = new QPushButton(tr("Create room"));
     connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject()));
     connect(btnOkay, SIGNAL(clicked()), this, SLOT(accept()));
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
         buttonLayout->addWidget(btnCancel);
         buttonLayout->addWidget(btnOkay);
 #else
--- a/QTfrontend/ui/widget/seedprompt.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/ui/widget/seedprompt.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -62,7 +62,7 @@
         QPushButton * btnOkay = new QPushButton(tr("Set seed"));
         connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject()));
         connect(btnOkay, SIGNAL(clicked()), this, SLOT(accept()));
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
         buttonLayout->addWidget(btnCancel);
         buttonLayout->addWidget(btnOkay);
 #else
--- a/QTfrontend/util/FileEngine.cpp	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/util/FileEngine.cpp	Mon Jun 17 16:56:58 2013 +0200
@@ -54,7 +54,7 @@
     }
 
     if (!m_handle) {
-        qWarning("[PHYSFS] Failed to open %s, reason: %s", m_fileName.toUtf8().constData(), PHYSFS_getLastError());
+        qWarning(QString("[PHYSFS] Failed to open %1, reason: %2").arg(m_fileName).arg(FileEngineHandler::errorStr()).toLocal8Bit().constData());
         return false;
     }
 
@@ -310,6 +310,8 @@
 FileEngineHandler::FileEngineHandler(char *argv0)
 {
     PHYSFS_init(argv0);
+
+    qDebug(QString("[PHYSFS] Init: %1").arg(errorStr()).toLocal8Bit().constData());
 }
 
 FileEngineHandler::~FileEngineHandler()
@@ -328,16 +330,19 @@
 void FileEngineHandler::mount(const QString &path)
 {
     PHYSFS_mount(path.toUtf8().constData(), NULL, 0);
+    qDebug(QString("[PHYSFS] Mounting '%1' to '/': %2").arg(path).arg(errorStr()).toLocal8Bit().constData());
 }
 
 void FileEngineHandler::mount(const QString & path, const QString & mountPoint)
 {
     PHYSFS_mount(path.toUtf8().constData(), mountPoint.toUtf8().constData(), 0);
+    qDebug(QString("[PHYSFS] Mounting '%1' to '%2': %3").arg(path).arg(mountPoint).arg(errorStr()).toLocal8Bit().data());
 }
 
 void FileEngineHandler::setWriteDir(const QString &path)
 {
     PHYSFS_setWriteDir(path.toUtf8().constData());
+    qDebug(QString("[PHYSFS] Setting write dir to '%1': %2").arg(path).arg(errorStr()).toLocal8Bit().data());
 }
 
 void FileEngineHandler::mountPacks()
@@ -345,6 +350,12 @@
     hedgewarsMountPackages();
 }
 
+QString FileEngineHandler::errorStr()
+{
+    QString s = QString::fromUtf8(PHYSFS_getLastError());
+    return s.isEmpty() ? "ok" : s;
+}
+
 
 FileEngineIterator::FileEngineIterator(QDir::Filters filters, const QStringList &nameFilters, const QStringList &entries)
     : QAbstractFileEngineIterator(filters, nameFilters)
--- a/QTfrontend/util/FileEngine.h	Sun Jun 16 00:46:11 2013 +0400
+++ b/QTfrontend/util/FileEngine.h	Mon Jun 17 16:56:58 2013 +0200
@@ -71,6 +71,7 @@
         static void mount(const QString & path, const QString & mountPoint);
         static void setWriteDir(const QString & path);
         static void mountPacks();
+        static QString errorStr();
 
 //    private:
         static const QString scheme;
--- a/cmake_modules/compilerchecks.cmake	Sun Jun 16 00:46:11 2013 +0400
+++ b/cmake_modules/compilerchecks.cmake	Mon Jun 17 16:56:58 2013 +0200
@@ -5,20 +5,25 @@
 
 # CMAKE_C{XX}_FLAGS is for compiler flags (c and c++)
 # CMAKE_EXE_LINKER_FLAGS is for linker flags (also add them to pascal_flags and haskell_flags)
-
+# CMAKE_SHARED_LIBRARY_<lang>_FLAGS same but for shared libraries
 
 #TODO: should there be two different checks for C and CXX?
 
-#stack protection
-check_c_compiler_flag("-fstack-protector" HAVE_STACKPROTECTOR)
-if(HAVE_STACKPROTECTOR)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector")
+#stack protection, when found it needs to go in the linker flags too
+#it is disabled on win32 because it adds a dll and messes with linker
+#(see 822312 654424 on bugzilla.redhat.com)
+check_c_compiler_flag("-fstack-protector-all -fstack-protector" HAVE_STACKPROTECTOR)
+if(HAVE_STACKPROTECTOR AND (NOT WIN32))
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-all -fstack-protector")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all -fstack-protector")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fstack-protector-all -fstack-protector")
+    set(CMAKE_SHARED_LIBRARY_C_FLAGS  "${CMAKE_SHARED_LIBRARY_C_FLAGS} -fstack-protector-all -fstack-protector")
+    set(CMAKE_SHARED_LIBRARY_CXX_FLAGS  "${CMAKE_SHARED_LIBRARY_C_FLAGS} -fstack-protector-all -fstack-protector")
 endif()
 
-#symbol visibility
-check_c_compiler_flag("-fvisibility=hidden" HAVE_VISIBILITYH)
-if(HAVE_VISIBILITYH)
+#symbol visibility, not supported on Windows (so we error out to avoid spam)
+check_c_compiler_flag("-fvisibility=hidden -Werror" HAVE_VISIBILITY)
+if(HAVE_VISIBILITY)
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
 endif()
--- a/cmake_modules/cpackvars.cmake	Sun Jun 16 00:46:11 2013 +0400
+++ b/cmake_modules/cpackvars.cmake	Mon Jun 17 16:56:58 2013 +0200
@@ -58,6 +58,7 @@
     "\\\\.db$"
     "\\\\.dof$"
     "\\\\.or$"
+    "\\\\.stackdump$"
     #archives
     "\\\\.zip$"
     "\\\\.gz$"
--- a/cmake_modules/platform.cmake	Sun Jun 16 00:46:11 2013 +0400
+++ b/cmake_modules/platform.cmake	Mon Jun 17 16:56:58 2013 +0200
@@ -73,8 +73,8 @@
 
 endif(APPLE)
 
-if(WINDOWS)
+if(WIN32)
     #this flags prevents a few dll hell problems
     set(CMAKE_C_FLAGS "-static-libgcc ${CMAKE_C_FLAGS}")
-endif(WINDOWS)
+endif(WIN32)
 
--- a/gameServer/Actions.hs	Sun Jun 16 00:46:11 2013 +0400
+++ b/gameServer/Actions.hs	Mon Jun 17 16:56:58 2013 +0200
@@ -174,8 +174,12 @@
 
     chans <- liftM (map sendChan) $ roomClientsS ri
     clNick <- client's nick
+    allClientsChans <- liftM (Prelude.map sendChan . Prelude.filter isVisible) $! allClientsS
 
-    processAction $ AnswerClients chans ["JOINED", clNick]
+    mapM_ processAction [
+        AnswerClients chans ["JOINED", clNick]
+        , AnswerClients allClientsChans ["CLIENT_FLAGS", "+i", clNick]
+        ]
 
 
 processAction (MoveToLobby msg) = do
@@ -196,6 +200,9 @@
         else
         mapM_ processAction [RemoveClientTeams, AnswerClients chans ["LEFT", clNick, msg]]
 
+    allClientsChans <- liftM (Prelude.map sendChan . Prelude.filter isVisible) $! allClientsS
+    processAction $ AnswerClients allClientsChans ["CLIENT_FLAGS", "-i", clNick]
+
     -- when not removing room
     ready <- client's isReady
     when (not master || playersNum > 1) . io $ do
--- a/hedgewars/uGearsList.pas	Sun Jun 16 00:46:11 2013 +0400
+++ b/hedgewars/uGearsList.pas	Mon Jun 17 16:56:58 2013 +0200
@@ -267,7 +267,7 @@
                         end;
                     State:= State or gstInvisible;
                     Health:= random(vobFrameTicks);
-                    Timer:= random(vobFramesCount);
+                    if gear^.Timer = 0 then Timer:= random(vobFramesCount);
                     Damage:= (random(2) * 2 - 1) * (vobVelocity + random(vobVelocity)) * 8;
                     end
                 end;
@@ -279,7 +279,7 @@
                 end;
          gtBee: begin
                 gear^.Radius:= 5;
-                gear^.Timer:= 500;
+                if gear^.Timer = 0 then gear^.Timer:= 500;
                 gear^.RenderTimer:= true;
                 gear^.Elasticity:= _0_9;
                 gear^.Tag:= 0;
@@ -288,16 +288,16 @@
                 gear^.Radius:= 250;
                 end;
  gtShotgunShot: begin
-                gear^.Timer:= 900;
+                if gear^.Timer = 0 then gear^.Timer:= 900;
                 gear^.Radius:= 2
                 end;
   gtPickHammer: begin
                 gear^.Radius:= 10;
-                gear^.Timer:= 4000
+                if gear^.Timer = 0 then gear^.Timer:= 4000
                 end;
    gtHammerHit: begin
                 gear^.Radius:= 8;
-                gear^.Timer:= 125
+                if gear^.Timer = 0 then gear^.Timer:= 125
                 end;
         gtRope: begin
                 gear^.Radius:= 3;
@@ -313,10 +313,13 @@
                 gear^.Elasticity:= _0_55;
                 gear^.Friction:= _0_995;
                 gear^.Density:= _1;
-                if cMinesTime < 0 then
-                    gear^.Timer:= getrandom(51)*100
-                else
-                    gear^.Timer:= cMinesTime;
+                if gear^.Timer = 0 then
+                    begin
+                    if cMinesTime < 0 then
+                        gear^.Timer:= getrandom(51)*100
+                    else
+                        gear^.Timer:= cMinesTime
+                    end
                 end;
        gtSMine: begin
                 gear^.Health:= 10;
@@ -325,7 +328,7 @@
                 gear^.Elasticity:= _0_55;
                 gear^.Friction:= _0_995;
                 gear^.Density:= _1_6;
-                gear^.Timer:= 500;
+                if gear^.Timer = 0 then gear^.Timer:= 500;
                 end;
        gtKnife: begin
                 gear^.Density:= _4;
@@ -336,7 +339,7 @@
                 gear^.nImpactSounds:= 1;
                 gear^.Radius:= 16;
                 gear^.Elasticity:= _0_3;
-                gear^.Timer:= 500
+                if gear^.Timer = 0 then gear^.Timer:= 500
                 end;
   gtExplosives: begin
                 gear^.ImpactSound:= sndGrenadeImpact;
@@ -361,7 +364,7 @@
                 gear^.Elasticity:= _0_55;
                 gear^.Friction:= _0_03;
                 gear^.Density:= _2;
-                gear^.Timer:= 5000;
+                if gear^.Timer = 0 then gear^.Timer:= 5000;
                 end;
      gtCluster: begin
                 gear^.Radius:= 2;
@@ -391,7 +394,7 @@
                 end;
    gtBlowTorch: begin
                 gear^.Radius:= cHHRadius + cBlowTorchC;
-                gear^.Timer:= 7500
+                if gear^.Timer = 0 then gear^.Timer:= 7500
                 end;
     gtSwitcher: begin
                 gear^.Z:= cCurrHHZ
@@ -401,10 +404,8 @@
                 gear^.nImpactSounds:= 1;
                 gear^.Radius:= 10;
                 gear^.Elasticity:= _0_3;
-                gear^.Timer:= 0
                 end;
       gtTardis: begin
-                gear^.Timer:= 0;
                 gear^.Pos:= 1;
                 gear^.Z:= cCurrHHZ+1;
                 end;
@@ -440,7 +441,7 @@
                 gear^.Friction:= _0_96;
                 gear^.Density:= _1_5;
                 gear^.RenderTimer:= true;
-                gear^.Timer:= 5000
+                if gear^.Timer = 0 then gear^.Timer:= 5000
                 end;
        gtDrill: begin
                 if gear^.Timer = 0 then
@@ -456,23 +457,23 @@
                 gear^.AdvBounce:= 1;
                 gear^.Radius:= 5;
                 gear^.Tag:= random(8);
-                gear^.Timer:= 5000;
+                if gear^.Timer = 0 then gear^.Timer:= 5000;
                 gear^.Elasticity:= _0_7;
                 gear^.Friction:= _0_995;
                 gear^.Density:= _1_5;
                 end;
      gtBallgun: begin
-                gear^.Timer:= 5001;
+                if gear^.Timer = 0 then gear^.Timer:= 5001;
                 end;
      gtRCPlane: begin
-                gear^.Timer:= 15000;
+                if gear^.Timer = 0 then gear^.Timer:= 15000;
                 gear^.Health:= 3;
                 gear^.Radius:= 8
                 end;
      gtJetpack: begin
                 gear^.Health:= 2000;
                 gear^.Damage:= 100;
-                gear^.State:= gstSubmersible
+                gear^.State:= Gear^.State or gstSubmersible
                 end;
      gtMolotov: begin
                 gear^.Radius:= 6;
@@ -480,7 +481,6 @@
                 end;
        gtBirdy: begin
                 gear^.Radius:= 16; // todo: check
-                gear^.Timer:= 0;
                 gear^.Health := 2000;
                 gear^.FlightTime := 2;
                 end;
@@ -513,18 +513,18 @@
                 end;
 gtFlamethrower: begin
                 gear^.Tag:= 10;
-                gear^.Timer:= 10;
+                if gear^.Timer = 0 then gear^.Timer:= 10;
                 gear^.Health:= 500;
                 gear^.Damage:= 100;
                 end;
      gtLandGun: begin
                 gear^.Tag:= 10;
-                gear^.Timer:= 10;
+                if gear^.Timer = 0 then gear^.Timer:= 10;
                 gear^.Health:= 1000;
                 gear^.Damage:= 100;
                 end;
  gtPoisonCloud: begin
-                gear^.Timer:= 5000;
+                if gear^.Timer = 0 then gear^.Timer:= 5000;
                 gear^.dY:= int2hwfloat(-4 + longint(getRandom(8))) / 1000;
                 end;
  gtResurrector: begin
@@ -535,7 +535,7 @@
                 gear^.Tag := 47;
                 end;
   gtNapalmBomb: begin
-                gear^.Timer:= 1000;
+                if gear^.Timer = 0 then gear^.Timer:= 1000;
                 gear^.Radius:= 5;
                 gear^.Density:= _1_5;
                 end;
--- a/hedgewars/uIO.pas	Sun Jun 16 00:46:11 2013 +0400
+++ b/hedgewars/uIO.pas	Mon Jun 17 16:56:58 2013 +0200
@@ -213,7 +213,7 @@
 end;
 
 procedure SendStat(sit: TStatInfoType; s: shortstring);
-const stc: array [TStatInfoType] of char = ('r', 'D', 'k', 'K', 'H', 'T', 'P', 's', 'S', 'B');
+const stc: array [TStatInfoType] of char = ('r', 'D', 'k', 'K', 'H', 'T', 'P', 's', 'S', 'B', 'c', 'g', 'p');
 var buf: shortstring;
 begin
 buf:= 'i' + stc[sit] + s;
--- a/hedgewars/uScript.pas	Sun Jun 16 00:46:11 2013 +0400
+++ b/hedgewars/uScript.pas	Mon Jun 17 16:56:58 2013 +0200
@@ -83,7 +83,8 @@
     SDLh,
     SysUtils,
     uIO,
-    uPhysFSLayer
+    uPhysFSLayer,
+    typinfo
     ;
 
 var luaState : Plua_State;
@@ -1285,6 +1286,64 @@
     lc_endgame:= 0
 end;
 
+function lc_sendstat(L : Plua_State) : LongInt; Cdecl;
+var statInfo : TStatInfoType;
+var i : LongInt;
+var color : shortstring;
+begin
+	statInfo := TStatInfoType(GetEnumValue(TypeInfo(TStatInfoType),lua_tostring(L, 1)));
+	if (lua_gettop(L) <> 2) and ((statInfo <> siPlayerKills) 
+			and (statInfo <> siClanHealth)) then
+        begin
+        LuaError('Lua: Wrong number of parameters passed to SendStat! Expected 2 parameters.');
+        end
+    else if (lua_gettop(L) <> 3) and ((statInfo = siPlayerKills) 
+			or (statInfo = siClanHealth)) then
+		begin
+        LuaError('Lua: Wrong number of parameters passed to SendStat! Expected 3 parameters.');
+        end
+    else
+		begin
+		if ((statInfo = siPlayerKills) or (statInfo = siClanHealth)) then
+			begin
+			// 3: team name
+			for i:= 0 to Pred(TeamsCount) do
+				begin
+				with TeamsArray[i]^ do
+					begin
+						if TeamName = lua_tostring(L, 3) then
+							begin
+							color := uUtils.IntToStr(Clan^.Color);
+							Break;
+							end
+					end				
+				end;
+			if (statInfo = siPlayerKills) then
+				begin
+					SendStat(siPlayerKills, color + ' ' +
+						lua_tostring(L, 2) + ' ' + TeamsArray[i]^.TeamName);
+				end
+			else if (statInfo = siClanHealth) then
+				begin
+					SendStat(siClanHealth, color + ' ' +
+						lua_tostring(L, 2));
+				end
+			end
+		else
+			begin
+			SendStat(statInfo,lua_tostring(L, 2));
+			end;
+		end;
+    lc_sendstat:= 0
+end;
+
+function lc_sendhealthstatsoff(L : Plua_State) : LongInt; Cdecl;
+begin
+    L:= L; // avoid compiler hint
+    uStats.SendHealthStatsOn := false;
+    lc_sendhealthstatsoff:= 0
+end;
+
 function lc_findplace(L : Plua_State) : LongInt; Cdecl;
 var gear: PGear;
     fall: boolean;
@@ -2382,6 +2441,8 @@
 lua_register(luaState, _P'WriteLnToConsole', @lc_writelntoconsole);
 lua_register(luaState, _P'GetGearType', @lc_getgeartype);
 lua_register(luaState, _P'EndGame', @lc_endgame);
+lua_register(luaState, _P'SendStat', @lc_sendstat);
+lua_register(luaState, _P'SendHealthStatsOff', @lc_sendhealthstatsoff);
 lua_register(luaState, _P'FindPlace', @lc_findplace);
 lua_register(luaState, _P'SetGearPosition', @lc_setgearposition);
 lua_register(luaState, _P'GetGearPosition', @lc_getgearposition);
--- a/hedgewars/uStats.pas	Sun Jun 16 00:46:11 2013 +0400
+++ b/hedgewars/uStats.pas	Mon Jun 17 16:56:58 2013 +0200
@@ -24,7 +24,8 @@
 
 var TotalRounds: LongInt;
     FinishedTurnsTotal: LongInt;
-
+    SendHealthStatsOn : boolean = true;
+    
 procedure initModule;
 procedure freeModule;
 
@@ -162,12 +163,13 @@
                 StepDamageRecv:= 0;
                 StepDamageGiven:= 0
                 end;
-
-for t:= 0 to Pred(ClansCount) do
-    with ClansArray[t]^ do
-        begin
-        SendStat(siClanHealth, IntToStr(Color) + ' ' + IntToStr(ClanHealth));
-        end;
+                
+if SendHealthStatsOn then
+	for t:= 0 to Pred(ClansCount) do
+		with ClansArray[t]^ do
+			begin
+			SendStat(siClanHealth, IntToStr(Color) + ' ' + IntToStr(ClanHealth));
+			end;
 
 Kills:= 0;
 KillsClan:= 0;
@@ -207,103 +209,104 @@
     maxTeamDamageName : shortstring;
     winnersClan : PClan;
 begin
-msd:= 0; msdhh:= nil;
-msk:= 0; mskhh:= nil;
-mskcnt:= 0;
-maxTeamKills := 0;
-maxTurnSkips := 0;
-maxTeamDamage := 0;
-winnersClan:= nil;
+if SendHealthStatsOn then
+	msd:= 0; msdhh:= nil;
+	msk:= 0; mskhh:= nil;
+	mskcnt:= 0;
+	maxTeamKills := 0;
+	maxTurnSkips := 0;
+	maxTeamDamage := 0;
+	winnersClan:= nil;
 
-for t:= 0 to Pred(TeamsCount) do
-    with TeamsArray[t]^ do
-    begin
-        if not ExtDriven then
-            SendStat(siTeamStats, GetTeamStatString(TeamsArray[t]));
-        for i:= 0 to cMaxHHIndex do
-            begin
-            if Hedgehogs[i].stats.MaxStepDamageGiven > msd then
-                begin
-                msdhh:= @Hedgehogs[i];
-                msd:= Hedgehogs[i].stats.MaxStepDamageGiven
-                end;
-            if Hedgehogs[i].stats.MaxStepKills >= msk then
-                if Hedgehogs[i].stats.MaxStepKills = msk then
-                    inc(mskcnt)
-                else
-                    begin
-                    mskcnt:= 1;
-                    mskhh:= @Hedgehogs[i];
-                    msk:= Hedgehogs[i].stats.MaxStepKills
-                    end;
-        end;
+	for t:= 0 to Pred(TeamsCount) do
+		with TeamsArray[t]^ do
+		begin
+			if not ExtDriven then
+				SendStat(siTeamStats, GetTeamStatString(TeamsArray[t]));
+			for i:= 0 to cMaxHHIndex do
+				begin
+				if Hedgehogs[i].stats.MaxStepDamageGiven > msd then
+					begin
+					msdhh:= @Hedgehogs[i];
+					msd:= Hedgehogs[i].stats.MaxStepDamageGiven
+					end;
+				if Hedgehogs[i].stats.MaxStepKills >= msk then
+					if Hedgehogs[i].stats.MaxStepKills = msk then
+						inc(mskcnt)
+					else
+						begin
+						mskcnt:= 1;
+						mskhh:= @Hedgehogs[i];
+						msk:= Hedgehogs[i].stats.MaxStepKills
+						end;
+			end;
 
-        { send player stats for winner teams }
-        if Clan^.ClanHealth > 0 then
-            begin
-            winnersClan:= Clan;
-            SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
-                IntToStr(stats.Kills) + ' ' + TeamName);
-        end;
+			{ send player stats for winner teams }
+			if Clan^.ClanHealth > 0 then
+				begin
+				winnersClan:= Clan;
+				SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
+					IntToStr(stats.Kills) + ' ' + TeamName);
+			end;
 
-        { determine maximum values of TeamKills, TurnSkips, TeamDamage }
-        if stats.TeamKills > maxTeamKills then
-            begin
-            maxTeamKills := stats.TeamKills;
-            maxTeamKillsName := TeamName;
-        end;
-        if stats.TurnSkips > maxTurnSkips then
-            begin
-            maxTurnSkips := stats.TurnSkips;
-            maxTurnSkipsName := TeamName;
-        end;
-        if stats.TeamDamage > maxTeamDamage then
-            begin
-            maxTeamDamage := stats.TeamDamage;
-            maxTeamDamageName := TeamName;
-        end;
+			{ determine maximum values of TeamKills, TurnSkips, TeamDamage }
+			if stats.TeamKills > maxTeamKills then
+				begin
+				maxTeamKills := stats.TeamKills;
+				maxTeamKillsName := TeamName;
+			end;
+			if stats.TurnSkips > maxTurnSkips then
+				begin
+				maxTurnSkips := stats.TurnSkips;
+				maxTurnSkipsName := TeamName;
+			end;
+			if stats.TeamDamage > maxTeamDamage then
+				begin
+				maxTeamDamage := stats.TeamDamage;
+				maxTeamDamageName := TeamName;
+			end;
 
-    end;
+		end;
 
-{ now send player stats for loser teams }
-for t:= 0 to Pred(TeamsCount) do
-    begin
-    with TeamsArray[t]^ do
-        begin
-        if Clan^.ClanHealth = 0 then
-            begin
-            SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
-                IntToStr(stats.Kills) + ' ' + TeamName);
-        end;
-    end;
-end;
+	{ now send player stats for loser teams }
+	for t:= 0 to Pred(TeamsCount) do
+		begin
+		with TeamsArray[t]^ do
+			begin
+			if Clan^.ClanHealth = 0 then
+				begin
+				SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
+					IntToStr(stats.Kills) + ' ' + TeamName);
+			end;
+		end;
+	end;
 
-if msdhh <> nil then
-    SendStat(siMaxStepDamage, IntToStr(msd) + ' ' + msdhh^.Name + ' (' + msdhh^.Team^.TeamName + ')');
-if mskcnt = 1 then
-    SendStat(siMaxStepKills, IntToStr(msk) + ' ' + mskhh^.Name + ' (' + mskhh^.Team^.TeamName + ')');
+	if msdhh <> nil then
+		SendStat(siMaxStepDamage, IntToStr(msd) + ' ' + msdhh^.Name + ' (' + msdhh^.Team^.TeamName + ')');
+	if mskcnt = 1 then
+		SendStat(siMaxStepKills, IntToStr(msk) + ' ' + mskhh^.Name + ' (' + mskhh^.Team^.TeamName + ')');
 
-if maxTeamKills > 1 then
-    SendStat(siMaxTeamKills, IntToStr(maxTeamKills) + ' ' + maxTeamKillsName);
-if maxTurnSkips > 2 then
-    SendStat(siMaxTurnSkips, IntToStr(maxTurnSkips) + ' ' + maxTurnSkipsName);
-if maxTeamDamage > 30 then
-    SendStat(siMaxTeamDamage, IntToStr(maxTeamDamage) + ' ' + maxTeamDamageName);
+	if maxTeamKills > 1 then
+		SendStat(siMaxTeamKills, IntToStr(maxTeamKills) + ' ' + maxTeamKillsName);
+	if maxTurnSkips > 2 then
+		SendStat(siMaxTurnSkips, IntToStr(maxTurnSkips) + ' ' + maxTurnSkipsName);
+	if maxTeamDamage > 30 then
+		SendStat(siMaxTeamDamage, IntToStr(maxTeamDamage) + ' ' + maxTeamDamageName);
 
-if KilledHHs > 0 then
-    SendStat(siKilledHHs, IntToStr(KilledHHs));
+	if KilledHHs > 0 then
+		SendStat(siKilledHHs, IntToStr(KilledHHs));
 
-// now to console
-if winnersClan <> nil then 
-    begin
-    WriteLnToConsole('WINNERS');
-    for t:= 0 to winnersClan^.TeamsNumber - 1 do
-        WriteLnToConsole(winnersClan^.Teams[t]^.TeamName);
-    end
-else
-    WriteLnToConsole('DRAW');
+	// now to console
+	if winnersClan <> nil then 
+		begin
+		WriteLnToConsole('WINNERS');
+		for t:= 0 to winnersClan^.TeamsNumber - 1 do
+			WriteLnToConsole(winnersClan^.Teams[t]^.TeamName);
+		end
+	else
+		WriteLnToConsole('DRAW');
 
-WriteLnToConsole('');
+	WriteLnToConsole('');
 end;
 
 procedure initModule;
--- a/hedgewars/uTypes.pas	Sun Jun 16 00:46:11 2013 +0400
+++ b/hedgewars/uTypes.pas	Mon Jun 17 16:56:58 2013 +0200
@@ -165,7 +165,8 @@
 
     TStatInfoType = (siGameResult, siMaxStepDamage, siMaxStepKills, siKilledHHs,
             siClanHealth, siTeamStats, siPlayerKills, siMaxTeamDamage,
-            siMaxTeamKills, siMaxTurnSkips );
+            siMaxTeamKills, siMaxTurnSkips, siCustomAchievement, siGraphTitle,
+            siPointType);
 
     // Various "emote" animations a hedgehog can do
     TWave = (waveRollup, waveSad, waveWave, waveHurrah, waveLemonade, waveShrug, waveJuggle);
--- a/misc/libphysfs/CMakeLists.txt	Sun Jun 16 00:46:11 2013 +0400
+++ b/misc/libphysfs/CMakeLists.txt	Mon Jun 17 16:56:58 2013 +0200
@@ -48,11 +48,13 @@
     # Fallback to older OS X on PowerPC to support wider range of systems...
     if(CMAKE_OSX_ARCHITECTURES MATCHES ppc)
         add_definitions(-DMAC_OS_X_VERSION_MIN_REQUIRED=1020)
-        set(OTHER_LDFLAGS ${OTHER_LDFLAGS} " -mmacosx-version-min=10.2")
+        list(APPEND OTHER_LDFLAGS "-mmacosx-version-min=10.2")
     endif(CMAKE_OSX_ARCHITECTURES MATCHES ppc)
 
     # Need these everywhere...
     add_definitions(-fno-common)
+    find_library(foundation_framework NAMES Foundation)
+    list(APPEND OTHER_LDFLAGS ${foundation_framework})
     find_library(iokit_framework NAMES IOKit)
     list(APPEND OTHER_LDFLAGS ${iokit_framework})
 endif(MACOSX)
--- a/misc/libphysfs/platform_macosx.c	Sun Jun 16 00:46:11 2013 +0400
+++ b/misc/libphysfs/platform_macosx.c	Mon Jun 17 16:56:58 2013 +0200
@@ -44,6 +44,31 @@
     fflush(stderr);
     abort();
 }
+
+
+/* apparently libssp is missing from 10.4 SDK
+   code from http://wiki.osdev.org/GCC_Stack_Smashing_Protector */
+void * __stack_chk_guard = NULL;
+
+void __stack_chk_guard_setup()
+{
+    unsigned char * p;
+    p = (unsigned char *) &__stack_chk_guard;
+
+    /* If you have the ability to generate random numbers in your kernel then use them,
+       otherwise for 32-bit code: *p = 0x00000aff; */
+    *p = random();
+}
+
+void __attribute__((noreturn)) __stack_chk_fail()
+{
+    /* put your panic function or similar in here */
+    unsigned char * vid = (unsigned char *)0xB8000;
+    vid[1] = 7;
+    for(;;)
+    vid[0]++;
+}
+
 #endif
 #endif /* __APPLE__ */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Locale/campaigns_en.txt	Mon Jun 17 16:56:58 2013 +0200
@@ -0,0 +1,19 @@
+A Classic Fairytale-first_blood.desc="Help Leaks a lot to complete his training and become a proper hedgehog warrior. You will be trained in the art of rope, parachute, shoryuken and desert eagle."
+
+A Classic Fairytale-shadow.desc="Leaks a lot and Dense Cloud are going for hunting. Be prepared for the dangers awaiting you at the forest. Remember, make your choices wisely."
+
+A Classic Fairytale-journey.desc="Leaks a lot has to go to the other side of the island. Be fast and cautious."
+
+A Classic Fairytale-united.desc="After his long journey Leaks a lot is finally back to the village. However, there isn't time to rest. You have to defend the village from the rage of the cannibals."
+
+A Classic Fairytale-backstab.desc="The monstrous cannibals are hunting Leaks a lot and his friends. Defeat them once again and protect your allies. Use your resources accordingly to defeat the incoming enemies!"
+
+A Classic Fairytale-dragon.desc="Leaks a lot has to get to the other side of the lake. Become a rope master and avoid get hit by the enemy shots."
+
+A Classic Fairytale-family.desc="Leaks a lot has to save once more his allies. Eliminate the enemy hogs and free your comrades. Use your resources carefully as they are limited. Drill some holes in the right spot and go close to the princess."
+
+A Classic Fairytale-queen.desc="Leaks a lot has to fight once again. In order to win he'll have to fight the traitor and use all the resources available. Defeat the enemy!"
+
+A Classic Fairytale-enemy.desc="What a great twist! Leaks a lot has to fight side by side with the… “cannibals” against the common enemy. The evil cyborgs!"
+
+A Classic Fairytale-epil.desc="Congratulations! Leaks a lot can finally leave in peace and get praised by his new friends and his tribe. Be proud for what you succeed! You can play again previous missions and see the other possible endings."