--- a/INSTALL Thu Aug 30 12:47:41 2012 -0400
+++ b/INSTALL Thu Aug 30 13:02:19 2012 -0400
@@ -18,9 +18,9 @@
$ cmake .
or
$ cmake -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="install_prefix" \
--DDATA_INSTALL_DIR="data_dir" .
+-DDATA_INSTALL_DIR="data_dir" -DNOSERVER=1 .
-add -DWITH_SERVER=1 to compile net server; if you have Qt installed but it is
+add -DNOSERVER=0 to compile net server; if you have Qt installed but it is
not found you can set it up with -DQT_QMAKE_EXECUTABLE="path_to_qmake"
2. Compile:
--- a/QTfrontend/CMakeLists.txt Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/CMakeLists.txt Thu Aug 30 13:02:19 2012 -0400
@@ -28,6 +28,7 @@
# Configure for SDL
find_package(SDL REQUIRED)
find_package(SDL_mixer REQUIRED)
+find_package(FFMPEG)
include_directories(.)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/model)
@@ -39,6 +40,7 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/util)
include_directories(${SDL_INCLUDE_DIR})
include_directories(${SDLMIXER_INCLUDE_DIR})
+include_directories(${FFMPEG_INCLUDE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/misc/quazip)
if(UNIX)
# HACK: in freebsd cannot find iconv.h included via SDL.h
@@ -71,6 +73,10 @@
file(GLOB_RECURSE UIcpp ui/*.cpp)
file(GLOB UtilCpp util/*.cpp)
+if((NOT NO_VIDEOREC) AND "${FFMPEG_FOUND}")
+ add_definitions(-DVIDEOREC)
+endif()
+
set(hwfr_src
${ModelCpp}
${NetCpp}
@@ -172,6 +178,7 @@
${QT_LIBRARIES}
${SDL_LIBRARY}
${SDLMIXER_LIBRARY}
+ ${FFMPEG_LIBRARIES}
${HW_LINK_LIBS}
)
--- a/QTfrontend/binds.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/binds.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -65,5 +65,6 @@
{"mute", "8", QT_TRANSLATE_NOOP("binds", "mute audio"), NULL, NULL},
{"fullscr", "f12", QT_TRANSLATE_NOOP("binds", "change mode"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle fullscreen mode:")},
{"capture", "c", QT_TRANSLATE_NOOP("binds", "capture"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Take a screenshot:")},
- {"rotmask", "delete", QT_TRANSLATE_NOOP("binds", "hedgehogs\ninfo"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle labels above hedgehogs:")}
+ {"rotmask", "delete", QT_TRANSLATE_NOOP("binds", "hedgehogs\ninfo"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle labels above hedgehogs:")},
+ {"record", "r", QT_TRANSLATE_NOOP("binds", "record"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Record video:")}
};
--- a/QTfrontend/binds.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/binds.h Thu Aug 30 13:02:19 2012 -0400
@@ -21,7 +21,7 @@
#include <QString>
-#define BINDS_NUMBER 45
+#define BINDS_NUMBER 46
struct BindAction
{
--- a/QTfrontend/game.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/game.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -53,20 +53,20 @@
{
switch (gameType)
{
- case gtSave:
- if (gameState == gsInterrupted || gameState == gsHalted)
- emit HaveRecord(false, demo);
- else if (gameState == gsFinished)
- emit HaveRecord(true, demo);
- break;
case gtDemo:
+ // for video recording we need demo anyway
+ emit HaveRecord(rtNeither, demo);
break;
case gtNet:
- emit HaveRecord(true, demo);
+ emit HaveRecord(rtDemo, demo);
break;
default:
- if (gameState == gsInterrupted || gameState == gsHalted) emit HaveRecord(false, demo);
- else if (gameState == gsFinished) emit HaveRecord(true, demo);
+ if (gameState == gsInterrupted || gameState == gsHalted)
+ emit HaveRecord(rtSave, demo);
+ else if (gameState == gsFinished)
+ emit HaveRecord(rtDemo, demo);
+ else
+ emit HaveRecord(rtNeither, demo);
}
SetGameState(gsStopped);
}
--- a/QTfrontend/game.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/game.h Thu Aug 30 13:02:19 2012 -0400
@@ -40,6 +40,13 @@
gsHalted = 6
};
+enum RecordType
+{
+ rtDemo,
+ rtSave,
+ rtNeither,
+};
+
bool checkForDir(const QString & dir);
class HWGame : public TCPBase
@@ -70,7 +77,7 @@
void SendTeamMessage(const QString & msg);
void GameStateChanged(GameState gameState);
void GameStats(char type, const QString & info);
- void HaveRecord(bool isDemo, const QByteArray & record);
+ void HaveRecord(RecordType type, const QByteArray & record);
void ErrorMessage(const QString &);
void CampStateChanged(int);
--- a/QTfrontend/gameuiconfig.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/gameuiconfig.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -22,14 +22,17 @@
#include <QDesktopWidget>
#include <QInputDialog>
#include <QCryptographicHash>
+#include <QStandardItemModel>
#include "gameuiconfig.h"
#include "hwform.h"
#include "pageoptions.h"
+#include "pagevideos.h"
#include "pagenetserver.h"
#include "hwconsts.h"
#include "fpsedit.h"
#include "HWApplication.h"
+#include "DataManager.h"
GameUIConfig::GameUIConfig(HWForm * FormWidgets, const QString & fileName)
: QSettings(fileName, QSettings::IniFormat)
@@ -42,6 +45,7 @@
resizeToConfigValues();
reloadValues();
+ reloadVideosValues();
}
void GameUIConfig::reloadValues(void)
@@ -108,6 +112,35 @@
depth = HWApplication::desktop()->depth();
if (depth < 16) depth = 16;
else if (depth > 16) depth = 32;
+
+ { // load colors
+ QStandardItemModel * model = DataManager::instance().colorsModel();
+ for(int i = model->rowCount() - 1; i >= 0; --i)
+ model->item(i)->setData(QColor(value(QString("colors/color%1").arg(i), model->item(i)->data().value<QColor>()).value<QColor>()));
+ }
+}
+
+void GameUIConfig::reloadVideosValues(void)
+{
+ Form->ui.pageVideos->framerateBox->setValue(value("videorec/fps",25).toUInt());
+ bool useGameRes = value("videorec/usegameres",true).toBool();
+ if (useGameRes)
+ {
+ QRect res = vid_Resolution();
+ Form->ui.pageVideos->widthEdit->setText(QString::number(res.width()));
+ Form->ui.pageVideos->heightEdit->setText(QString::number(res.height()));
+ }
+ else
+ {
+ Form->ui.pageVideos->widthEdit->setText(value("videorec/width","800").toString());
+ Form->ui.pageVideos->heightEdit->setText(value("videorec/height","600").toString());
+ }
+ Form->ui.pageVideos->checkUseGameRes->setChecked(useGameRes);
+ Form->ui.pageVideos->checkRecordAudio->setChecked(value("videorec/audio",true).toBool());
+ if (!Form->ui.pageVideos->tryCodecs(value("videorec/format","no").toString(),
+ value("videorec/videocodec","no").toString(),
+ value("videorec/audiocodec","no").toString()))
+ Form->ui.pageVideos->setDefaultCodecs();
}
QStringList GameUIConfig::GetTeamsList()
@@ -182,6 +215,28 @@
#ifdef SPARKLE_ENABLED
setValue("misc/autoUpdate", isAutoUpdateEnabled());
#endif
+
+ { // save colors
+ QStandardItemModel * model = DataManager::instance().colorsModel();
+ for(int i = model->rowCount() - 1; i >= 0; --i)
+ setValue(QString("colors/color%1").arg(i), model->item(i)->data());
+ }
+
+ Form->gameSettings->sync();
+}
+
+void GameUIConfig::SaveVideosOptions()
+{
+ QRect res = rec_Resolution();
+ setValue("videorec/format", AVFormat());
+ setValue("videorec/videocodec", videoCodec());
+ setValue("videorec/audiocodec", audioCodec());
+ setValue("videorec/fps", rec_Framerate());
+ setValue("videorec/width", res.width());
+ setValue("videorec/height", res.height());
+ setValue("videorec/usegameres", Form->ui.pageVideos->checkUseGameRes->isChecked());
+ setValue("videorec/audio", recordAudio());
+
Form->gameSettings->sync();
}
@@ -380,3 +435,38 @@
{
return Form->ui.pageOptions->volumeBox->value() * 128 / 100;
}
+
+QString GameUIConfig::AVFormat()
+{
+ return Form->ui.pageVideos->format();
+}
+
+QString GameUIConfig::videoCodec()
+{
+ return Form->ui.pageVideos->videoCodec();
+}
+
+QString GameUIConfig::audioCodec()
+{
+ return Form->ui.pageVideos->audioCodec();
+}
+
+QRect GameUIConfig::rec_Resolution()
+{
+ if (Form->ui.pageVideos->checkUseGameRes->isChecked())
+ return vid_Resolution();
+ QRect res(0,0,0,0);
+ res.setWidth(Form->ui.pageVideos->widthEdit->text().toUInt());
+ res.setHeight(Form->ui.pageVideos->heightEdit->text().toUInt());
+ return res;
+}
+
+int GameUIConfig::rec_Framerate()
+{
+ return Form->ui.pageVideos->framerateBox->value();
+}
+
+bool GameUIConfig::recordAudio()
+{
+ return Form->ui.pageVideos->checkRecordAudio->isChecked();
+}
--- a/QTfrontend/gameuiconfig.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/gameuiconfig.h Thu Aug 30 13:02:19 2012 -0400
@@ -59,18 +59,27 @@
void resizeToConfigValues();
quint32 stereoMode() const;
+ QString AVFormat();
+ QString videoCodec();
+ QString audioCodec();
+ QRect rec_Resolution();
+ int rec_Framerate();
+ bool recordAudio();
+
#ifdef __APPLE__
#ifdef SPARKLE_ENABLED
bool isAutoUpdateEnabled();
#endif
#endif
- void reloadValues(void);
+ void reloadValues();
+ void reloadVideosValues();
signals:
void frontendFullscreen(bool value);
public slots:
void SaveOptions();
+ void SaveVideosOptions();
void updNetNick();
private:
bool netPasswordIsValid();
--- a/QTfrontend/hwform.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/hwform.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -77,6 +77,7 @@
#include "pagegamestats.h"
#include "pageplayrecord.h"
#include "pagedata.h"
+#include "pagevideos.h"
#include "hwconsts.h"
#include "newnetclient.h"
#include "gamecfgwidget.h"
@@ -91,6 +92,7 @@
#include "drawmapwidget.h"
#include "mouseoverfilter.h"
#include "roomslistmodel.h"
+#include "recorder.h"
#include "DataManager.h"
@@ -141,6 +143,8 @@
config = new GameUIConfig(this, cfgdir->absolutePath() + "/hedgewars.ini");
+ ui.pageVideos->init(config);
+
#ifdef __APPLE__
panel = new M3Panel;
@@ -200,12 +204,17 @@
connect(ui.pageNetGame, SIGNAL(DLCClicked()), pageSwitchMapper, SLOT(map()));
pageSwitchMapper->setMapping(ui.pageNetGame, ID_PAGE_DATADOWNLOAD);
+#ifdef VIDEOREC
+ connect(ui.pageMain->BtnVideos, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
+ pageSwitchMapper->setMapping(ui.pageMain->BtnVideos, ID_PAGE_VIDEOS);
+#endif
+
//connect(ui.pageMain->BtnExit, SIGNAL(pressed()), this, SLOT(btnExitPressed()));
//connect(ui.pageMain->BtnExit, SIGNAL(clicked()), this, SLOT(btnExitClicked()));
connect(ui.pageFeedback->BtnSend, SIGNAL(clicked()), this, SLOT(SendFeedback()));
- connect(ui.pageEditTeam, SIGNAL(teamEdited()), this, SLOT(AfterTeamEdit()));
+ connect(ui.pageEditTeam, SIGNAL(goBack()), this, SLOT(AfterTeamEdit()));
connect(ui.pageMultiplayer->BtnStartMPGame, SIGNAL(clicked()), this, SLOT(StartMPGame()));
connect(ui.pageMultiplayer->teamsSelect, SIGNAL(setEnabledGameStart(bool)),
@@ -291,6 +300,7 @@
connect(ui.pageConnecting, SIGNAL(cancelConnection()), this, SLOT(GoBack()));
+ connect(ui.pageVideos, SIGNAL(goBack()), config, SLOT(SaveVideosOptions()));
ammoSchemeModel = new AmmoSchemeModel(this, cfgdir->absolutePath() + "/schemes.ini");
ui.pageScheme->setModel(ammoSchemeModel);
@@ -515,6 +525,11 @@
GoToPage(ID_PAGE_SCHEME);
}
+void HWForm::GoToVideos()
+{
+ GoToPage(ID_PAGE_VIDEOS);
+}
+
void HWForm::OnPageShown(quint8 id, quint8 lastid)
{
#ifdef USE_XFIRE
@@ -606,6 +621,11 @@
config->reloadValues();
}
+ if (id == ID_PAGE_VIDEOS )
+ {
+ config->reloadVideosValues();
+ }
+
// load and save ignore/friends lists
if (lastid == ID_PAGE_NETGAME) // leaving a room
ui.pageNetGame->pChatWidget->saveLists(ui.pageOptions->editNetNick->text());
@@ -707,6 +727,8 @@
int curid = ui.Pages->currentIndex();
if (curid == ID_PAGE_MAIN)
{
+ if (!ui.pageVideos->tryQuit(this))
+ return;
stopAnim = true;
exit();
}
@@ -881,7 +903,7 @@
void HWForm::AfterTeamEdit()
{
UpdateTeamsLists();
- GoBack();
+ //GoBack();
}
@@ -1361,7 +1383,7 @@
connect(game, SIGNAL(GameStateChanged(GameState)), this, SLOT(GameStateChanged(GameState)));
connect(game, SIGNAL(GameStats(char, const QString &)), ui.pageGameStats, SLOT(GameStats(char, const QString &)));
connect(game, SIGNAL(ErrorMessage(const QString &)), this, SLOT(ShowErrorMessage(const QString &)), Qt::QueuedConnection);
- connect(game, SIGNAL(HaveRecord(bool, const QByteArray &)), this, SLOT(GetRecord(bool, const QByteArray &)));
+ connect(game, SIGNAL(HaveRecord(RecordType, const QByteArray &)), this, SLOT(GetRecord(RecordType, const QByteArray &)));
m_lastDemo = QByteArray();
}
@@ -1372,43 +1394,47 @@
msg);
}
-void HWForm::GetRecord(bool isDemo, const QByteArray & record)
+void HWForm::GetRecord(RecordType type, const QByteArray & record)
{
- QString filename;
- QByteArray demo = record;
- QString recordFileName =
- config->appendDateTimeToRecordName() ?
- QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm") :
- "LastRound";
+ if (type != rtNeither)
+ {
+ QString filename;
+ QByteArray demo = record;
+ QString recordFileName =
+ config->appendDateTimeToRecordName() ?
+ QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm") :
+ "LastRound";
- QStringList versionParts = cVersionString->split('-');
- if ( (versionParts.size() == 2) && (!versionParts[1].isEmpty()) && (versionParts[1].contains(':')) )
- recordFileName = recordFileName + "_" + versionParts[1].replace(':','-');
+ QStringList versionParts = cVersionString->split('-');
+ if ( (versionParts.size() == 2) && (!versionParts[1].isEmpty()) && (versionParts[1].contains(':')) )
+ recordFileName = recordFileName + "_" + versionParts[1].replace(':','-');
- if (isDemo)
- {
- demo.replace(QByteArray("\x02TL"), QByteArray("\x02TD"));
- demo.replace(QByteArray("\x02TN"), QByteArray("\x02TD"));
- demo.replace(QByteArray("\x02TS"), QByteArray("\x02TD"));
- filename = cfgdir->absolutePath() + "/Demos/" + recordFileName + "." + *cProtoVer + ".hwd";
- m_lastDemo = demo;
- }
- else
- {
- demo.replace(QByteArray("\x02TL"), QByteArray("\x02TS"));
- demo.replace(QByteArray("\x02TN"), QByteArray("\x02TS"));
- filename = cfgdir->absolutePath() + "/Saves/" + recordFileName + "." + *cProtoVer + ".hws";
+ if (type == rtDemo)
+ {
+ demo.replace(QByteArray("\x02TL"), QByteArray("\x02TD"));
+ demo.replace(QByteArray("\x02TN"), QByteArray("\x02TD"));
+ demo.replace(QByteArray("\x02TS"), QByteArray("\x02TD"));
+ filename = cfgdir->absolutePath() + "/Demos/" + recordFileName + "." + *cProtoVer + ".hwd";
+ m_lastDemo = demo;
+ }
+ else
+ {
+ demo.replace(QByteArray("\x02TL"), QByteArray("\x02TS"));
+ demo.replace(QByteArray("\x02TN"), QByteArray("\x02TS"));
+ filename = cfgdir->absolutePath() + "/Saves/" + recordFileName + "." + *cProtoVer + ".hws";
+ }
+
+ QFile demofile(filename);
+ if (!demofile.open(QIODevice::WriteOnly))
+ ShowErrorMessage(tr("Cannot save record to file %1").arg(filename));
+ else
+ {
+ demofile.write(demo);
+ demofile.close();
+ }
}
-
- QFile demofile(filename);
- if (!demofile.open(QIODevice::WriteOnly))
- {
- ShowErrorMessage(tr("Cannot save record to file %1").arg(filename));
- return ;
- }
- demofile.write(demo);
- demofile.close();
+ ui.pageVideos->startEncoding(record);
}
void HWForm::startTraining(const QString & scriptName)
@@ -1455,6 +1481,7 @@
xfire_free();
#endif
config->SaveOptions();
+ config->SaveVideosOptions();
event->accept();
}
--- a/QTfrontend/hwform.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/hwform.h Thu Aug 30 13:02:19 2012 -0400
@@ -67,6 +67,7 @@
void exit();
void setButtonDescription(QString desc);
void backDescription();
+ void GoToVideos();
private slots:
void GoToSaves();
@@ -114,7 +115,7 @@
void GameStateChanged(GameState gameState);
void ForcedDisconnect(const QString & reason);
void ShowErrorMessage(const QString &);
- void GetRecord(bool isDemo, const QByteArray & record);
+ void GetRecord(RecordType type, const QByteArray & record);
void CreateNetGame();
void UpdateWeapons();
void onFrontendFullscreen(bool value);
@@ -177,6 +178,7 @@
ID_PAGE_DRAWMAP ,
ID_PAGE_DATADOWNLOAD ,
ID_PAGE_FEEDBACK ,
+ ID_PAGE_VIDEOS,
MAX_PAGE
};
QPointer<HWGame> game;
--- a/QTfrontend/main.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/main.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -197,6 +197,8 @@
checkForDir(cfgdir->absolutePath() + "/Screenshots");
checkForDir(cfgdir->absolutePath() + "/Teams");
checkForDir(cfgdir->absolutePath() + "/Logs");
+ checkForDir(cfgdir->absolutePath() + "/Videos");
+ checkForDir(cfgdir->absolutePath() + "/VideoTemp");
}
datadir->cd(bindir->absolutePath());
--- a/QTfrontend/model/ammoSchemeModel.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/model/ammoSchemeModel.h Thu Aug 30 13:02:19 2012 -0400
@@ -47,8 +47,8 @@
public slots:
void Save();
- signals:
- void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
+// signals:
+// void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
protected:
QList< QList<QVariant> > schemes;
--- a/QTfrontend/net/newnetclient.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/net/newnetclient.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -384,38 +384,6 @@
return;
}
- if (lst[0] == "ADD_TEAM")
- {
- if(lst.size() != 24)
- {
- qWarning("Net: Bad ADDTEAM message");
- return;
- }
- QStringList tmp = lst;
- tmp.removeFirst();
- emit AddNetTeam(tmp);
- return;
- }
-
- if (lst[0] == "REMOVE_TEAM")
- {
- if(lst.size() != 2)
- {
- qWarning("Net: Bad REMOVETEAM message");
- return;
- }
- emit RemoveNetTeam(HWTeam(lst[1]));
- return;
- }
-
- if(lst[0] == "ROOMABANDONED")
- {
- netClientState = InLobby;
- askRoomsList();
- emit LeftRoom(tr("Room destroyed"));
- return;
- }
-
if(lst[0] == "KICKED")
{
netClientState = InLobby;
@@ -424,31 +392,6 @@
return;
}
- if(lst[0] == "JOINED")
- {
- if(lst.size() < 2)
- {
- qWarning("Net: Bad JOINED message");
- return;
- }
-
- for(int i = 1; i < lst.size(); ++i)
- {
- if (lst[i] == mynick)
- {
- netClientState = InRoom;
- emit EnteredGame();
- emit roomMaster(isChief);
- if (isChief)
- emit configAsked();
- }
-
- emit nickAdded(lst[i], isChief && (lst[i] != mynick));
- emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
- }
- return;
- }
-
if(lst[0] == "LOBBY:JOINED")
{
if(lst.size() < 2)
@@ -472,21 +415,6 @@
return;
}
- if(lst[0] == "LEFT")
- {
- if(lst.size() < 2)
- {
- qWarning("Net: Bad LEFT message");
- return;
- }
- emit nickRemoved(lst[1]);
- if (lst.size() < 3)
- emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
- else
- emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
- return;
- }
-
if(lst[0] == "ROOM" && lst.size() == 10 && lst[1] == "ADD")
{
QStringList tmp = lst;
@@ -529,13 +457,6 @@
return;
}
- if (lst[0] == "RUN_GAME")
- {
- netClientState = InGame;
- emit AskForRunGame();
- return;
- }
-
if (lst[0] == "ASKPASSWORD")
{
emit AskForPassword(mynick);
@@ -563,76 +484,6 @@
return;
}
- if (lst[0] == "TEAM_ACCEPTED")
- {
- if (lst.size() != 2)
- {
- qWarning("Net: Bad TEAM_ACCEPTED message");
- return;
- }
- emit TeamAccepted(lst[1]);
- return;
- }
-
-
- if (lst[0] == "CFG")
- {
- if(lst.size() < 3)
- {
- qWarning("Net: Bad CFG message");
- return;
- }
- QStringList tmp = lst;
- tmp.removeFirst();
- tmp.removeFirst();
- if (lst[1] == "SCHEME")
- emit netSchemeConfig(tmp);
- else
- emit paramChanged(lst[1], tmp);
- return;
- }
-
- if (lst[0] == "HH_NUM")
- {
- if (lst.size() != 3)
- {
- qWarning("Net: Bad TEAM_ACCEPTED message");
- return;
- }
- HWTeam tmptm(lst[1]);
- tmptm.setNumHedgehogs(lst[2].toUInt());
- emit hhnumChanged(tmptm);
- return;
- }
-
- if (lst[0] == "TEAM_COLOR")
- {
- if (lst.size() != 3)
- {
- qWarning("Net: Bad TEAM_COLOR message");
- return;
- }
- HWTeam tmptm(lst[1]);
- tmptm.setColor(lst[2].toInt());
- emit teamColorChanged(tmptm);
- return;
- }
-
- if (lst[0] == "EM")
- {
- if(lst.size() < 2)
- {
- qWarning("Net: Bad EM message");
- return;
- }
- for(int i = 1; i < lst.size(); ++i)
- {
- QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
- emit FromNet(em);
- }
- return;
- }
-
if (lst[0] == "BYE")
{
if (lst.size() < 2)
@@ -650,26 +501,192 @@
return;
}
-
if (lst[0] == "ADMIN_ACCESS")
{
emit adminAccess(true);
return;
}
- if (lst[0] == "ROOM_CONTROL_ACCESS")
+ if(netClientState == InLobby && lst[0] == "JOINED")
{
- if (lst.size() < 2)
+ if(lst.size() < 2 || lst[1] != mynick)
{
- qWarning("Net: Bad ROOM_CONTROL_ACCESS message");
+ qWarning("Net: Bad JOINED message");
return;
}
- isChief = (lst[1] != "0");
- emit roomMaster(isChief);
+
+ for(int i = 1; i < lst.size(); ++i)
+ {
+ if (lst[i] == mynick)
+ {
+ netClientState = InRoom;
+ emit EnteredGame();
+ emit roomMaster(isChief);
+ if (isChief)
+ emit configAsked();
+ }
+
+ emit nickAdded(lst[i], isChief && (lst[i] != mynick));
+ emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
+ }
return;
}
- qWarning() << "Net: Unknown message:" << lst;
+ if(netClientState == InRoom || netClientState == InGame)
+ {
+ if (lst[0] == "EM")
+ {
+ if(lst.size() < 2)
+ {
+ qWarning("Net: Bad EM message");
+ return;
+ }
+ for(int i = 1; i < lst.size(); ++i)
+ {
+ QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
+ emit FromNet(em);
+ }
+ return;
+ }
+
+ if (lst[0] == "ADD_TEAM")
+ {
+ if(lst.size() != 24)
+ {
+ qWarning("Net: Bad ADDTEAM message");
+ return;
+ }
+ QStringList tmp = lst;
+ tmp.removeFirst();
+ emit AddNetTeam(tmp);
+ return;
+ }
+
+ if (lst[0] == "REMOVE_TEAM")
+ {
+ if(lst.size() != 2)
+ {
+ qWarning("Net: Bad REMOVETEAM message");
+ return;
+ }
+ emit RemoveNetTeam(HWTeam(lst[1]));
+ return;
+ }
+
+ if(lst[0] == "ROOMABANDONED")
+ {
+ netClientState = InLobby;
+ askRoomsList();
+ emit LeftRoom(tr("Room destroyed"));
+ return;
+ }
+
+ if (lst[0] == "RUN_GAME")
+ {
+ netClientState = InGame;
+ emit AskForRunGame();
+ return;
+ }
+
+ if (lst[0] == "TEAM_ACCEPTED")
+ {
+ if (lst.size() != 2)
+ {
+ qWarning("Net: Bad TEAM_ACCEPTED message");
+ return;
+ }
+ emit TeamAccepted(lst[1]);
+ return;
+ }
+
+ if (lst[0] == "CFG")
+ {
+ if(lst.size() < 3)
+ {
+ qWarning("Net: Bad CFG message");
+ return;
+ }
+ QStringList tmp = lst;
+ tmp.removeFirst();
+ tmp.removeFirst();
+ if (lst[1] == "SCHEME")
+ emit netSchemeConfig(tmp);
+ else
+ emit paramChanged(lst[1], tmp);
+ return;
+ }
+
+ if (lst[0] == "HH_NUM")
+ {
+ if (lst.size() != 3)
+ {
+ qWarning("Net: Bad TEAM_ACCEPTED message");
+ return;
+ }
+ HWTeam tmptm(lst[1]);
+ tmptm.setNumHedgehogs(lst[2].toUInt());
+ emit hhnumChanged(tmptm);
+ return;
+ }
+
+ if (lst[0] == "TEAM_COLOR")
+ {
+ if (lst.size() != 3)
+ {
+ qWarning("Net: Bad TEAM_COLOR message");
+ return;
+ }
+ HWTeam tmptm(lst[1]);
+ tmptm.setColor(lst[2].toInt());
+ emit teamColorChanged(tmptm);
+ return;
+ }
+
+ if(lst[0] == "JOINED")
+ {
+ if(lst.size() < 2)
+ {
+ qWarning("Net: Bad JOINED message");
+ return;
+ }
+
+ for(int i = 1; i < lst.size(); ++i)
+ {
+ emit nickAdded(lst[i], isChief && (lst[i] != mynick));
+ emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
+ }
+ return;
+ }
+
+ if(lst[0] == "LEFT")
+ {
+ if(lst.size() < 2)
+ {
+ qWarning("Net: Bad LEFT message");
+ return;
+ }
+ emit nickRemoved(lst[1]);
+ if (lst.size() < 3)
+ emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
+ else
+ emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
+ return;
+ }
+
+ if (lst[0] == "ROOM_CONTROL_ACCESS")
+ {
+ if (lst.size() < 2)
+ {
+ qWarning("Net: Bad ROOM_CONTROL_ACCESS message");
+ return;
+ }
+ isChief = (lst[1] != "0");
+ emit roomMaster(isChief);
+ return;
+ }
+ }
+
+ qWarning() << "Net: Unknown message or wrong state:" << lst;
}
void HWNewNet::onHedgehogsNumChanged(const HWTeam& team)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/net/recorder.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,129 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QString>
+#include <QByteArray>
+//#include <QMessageBox>
+
+#include "recorder.h"
+#include "gameuiconfig.h"
+#include "hwconsts.h"
+#include "game.h"
+#include "libav_iteraction.h"
+
+// Encoding is memory expensive process, so we need to limit maximum number
+// of simultaneous encoders.
+static const int maxRecorders = 3;
+static int numRecorders = 0;
+
+static QList<HWRecorder*> queue;
+
+HWRecorder::HWRecorder(GameUIConfig * config, const QString &prefix) :
+ TCPBase(false)
+{
+ this->config = config;
+ this->prefix = prefix;
+ finished = false;
+ name = prefix + "." + LibavIteraction::instance().getExtension(config->AVFormat());
+}
+
+HWRecorder::~HWRecorder()
+{
+ emit encodingFinished(finished);
+ if (queue.empty())
+ numRecorders--;
+ else
+ queue.takeFirst()->Start();
+}
+
+void HWRecorder::onClientDisconnect()
+{
+}
+
+void HWRecorder::onClientRead()
+{
+ quint8 msglen;
+ quint32 bufsize;
+ while (!readbuffer.isEmpty() && ((bufsize = readbuffer.size()) > 0) &&
+ ((msglen = readbuffer.data()[0]) < bufsize))
+ {
+ QByteArray msg = readbuffer.left(msglen + 1);
+ readbuffer.remove(0, msglen + 1);
+ switch (msg.at(1))
+ {
+ case '?':
+ SendIPC("!");
+ break;
+ case 'p':
+ emit onProgress((quint8(msg.at(2))*256.0 + quint8(msg.at(3)))*0.0001);
+ break;
+ case 'v':
+ finished = true;
+ break;
+ }
+ }
+}
+
+void HWRecorder::EncodeVideo(const QByteArray & record)
+{
+ toSendBuf = record;
+ toSendBuf.replace(QByteArray("\x02TD"), QByteArray("\x02TV"));
+ toSendBuf.replace(QByteArray("\x02TL"), QByteArray("\x02TV"));
+ toSendBuf.replace(QByteArray("\x02TN"), QByteArray("\x02TV"));
+ toSendBuf.replace(QByteArray("\x02TS"), QByteArray("\x02TV"));
+
+ if (numRecorders < maxRecorders)
+ {
+ numRecorders++;
+ Start(); // run engine
+ }
+ else
+ queue.push_back(this);
+}
+
+QStringList HWRecorder::getArguments()
+{
+ QStringList arguments;
+ QRect resolution = config->rec_Resolution();
+ arguments << cfgdir->absolutePath();
+ arguments << QString::number(resolution.width());
+ arguments << QString::number(resolution.height());
+ arguments << "32"; // bpp
+ arguments << QString("%1").arg(ipc_port);
+ arguments << "0"; // fullscreen
+ arguments << "0"; // sound
+ arguments << "0"; // music
+ arguments << "0"; // sound volume
+ arguments << QString::number(config->timerInterval());
+ arguments << datadir->absolutePath();
+ arguments << (config->isShowFPSEnabled() ? "1" : "0");
+ arguments << (config->isAltDamageEnabled() ? "1" : "0");
+ arguments << config->netNick().toUtf8().toBase64();
+ arguments << QString::number(config->translateQuality());
+ arguments << QString::number(config->stereoMode());
+ arguments << HWGame::tr("en.txt");
+ arguments << QString::number(config->rec_Framerate()); // framerate numerator
+ arguments << "1"; // framerate denominator
+ arguments << prefix;
+ arguments << config->AVFormat();
+ arguments << config->videoCodec();
+ arguments << "5"; // video quality
+ arguments << (config->recordAudio()? config->audioCodec() : "no");
+
+ return arguments;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/net/recorder.h Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,58 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef RECORDER_H
+#define RECORDER_H
+
+#include <QString>
+#include <QByteArray>
+
+#include "tcpBase.h"
+
+class GameUIConfig;
+class VideoItem;
+
+class HWRecorder : public TCPBase
+{
+ Q_OBJECT
+ public:
+ HWRecorder(GameUIConfig * config, const QString & prefix);
+ virtual ~HWRecorder();
+
+ void EncodeVideo(const QByteArray & record);
+
+ VideoItem * item; // used by pagevideos
+ QString name;
+ QString prefix;
+
+ protected:
+ // virtuals from TCPBase
+ virtual QStringList getArguments();
+ virtual void onClientRead();
+ virtual void onClientDisconnect();
+
+ signals:
+ void onProgress(float progress); // 0 < progress < 1
+ void encodingFinished(bool success);
+
+ private:
+ bool finished;
+ GameUIConfig * config;
+};
+
+#endif // RECORDER_H
--- a/QTfrontend/net/tcpBase.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/net/tcpBase.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -31,6 +31,8 @@
TCPBase::~TCPBase()
{
+ if (IPCSocket)
+ IPCSocket->deleteLater();
}
TCPBase::TCPBase(bool demoMode) :
@@ -65,6 +67,9 @@
connect(IPCSocket, SIGNAL(disconnected()), this, SLOT(ClientDisconnect()));
connect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
SendToClientFirst();
+
+ if(srvsList.size()==1) srvsList.pop_front();
+ emit isReadyNow();
}
void TCPBase::RealStart()
@@ -88,8 +93,8 @@
disconnect(IPCSocket, SIGNAL(readyRead()), this, SLOT(ClientRead()));
onClientDisconnect();
- if(srvsList.size()==1) srvsList.pop_front();
- emit isReadyNow();
+ /* if(srvsList.size()==1) srvsList.pop_front();
+ emit isReadyNow();*/
IPCSocket->deleteLater();
deleteLater();
}
@@ -107,6 +112,8 @@
QMessageBox::critical(0, tr("Error"),
tr("Unable to run engine: %1 (")
.arg(error) + bindir->absolutePath() + "/hwengine)");
+
+ ClientDisconnect();
}
void TCPBase::tcpServerReady()
--- a/QTfrontend/res/css/birthday.css Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/birthday.css Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
a { color:#c8c8ff; }
QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
background-color: rgba(13, 5, 68, 70%);
}
@@ -42,7 +42,7 @@
}
QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
QComboBox QAbstractItemView, IconedGroupBox,
.QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
}
QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
border-radius: 10px;
}
QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
font: bold 13px;
}
SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/christmas.css Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/christmas.css Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
a { color:#c8c8ff; }
QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
background-color: rgba(13, 5, 68, 70%);
}
@@ -42,7 +42,7 @@
}
QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
QComboBox QAbstractItemView, IconedGroupBox,
.QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
}
QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
border-radius: 10px;
}
QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
font: bold 13px;
}
SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/easter.css Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/easter.css Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
a { color:#c8c8ff; }
QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
background-color: rgba(13, 5, 68, 70%);
}
@@ -42,7 +42,7 @@
}
QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
QComboBox QAbstractItemView, IconedGroupBox,
.QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
}
QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
border-radius: 10px;
}
QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
font: bold 13px;
}
SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/qt.css Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/res/css/qt.css Thu Aug 30 13:02:19 2012 -0400
@@ -33,7 +33,7 @@
a { color:#c8c8ff; }
QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
background-color: rgba(13, 5, 68, 70%);
}
@@ -42,7 +42,7 @@
}
QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
QComboBox QAbstractItemView, IconedGroupBox,
.QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
}
QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
border-radius: 10px;
}
QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
font: bold 13px;
}
SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/ask_quit.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,79 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QTimer>
+
+#include "hwform.h"
+#include "ask_quit.h"
+#include "pagevideos.h"
+
+HWAskQuitDialog::HWAskQuitDialog(QWidget* parent, HWForm * form) : QDialog(parent)
+{
+ this->form = form;
+
+ setWindowTitle(tr("Do yot really want to quit?"));
+
+ QVBoxLayout * layout = new QVBoxLayout(this);
+
+ QLabel * lbLabel = new QLabel(this);
+ lbLabel->setText(QLabel::tr("There are videos that are currently being processed.\n"
+ "Exiting now will abort them.\n"
+ "Do yot really want to quit?"));
+ layout->addWidget(lbLabel);
+
+ lbList = new QLabel(this);
+ layout->addWidget(lbList);
+ updateList();
+
+ QDialogButtonBox* dbbButtons = new QDialogButtonBox(this);
+ QPushButton * pbYes = dbbButtons->addButton(QDialogButtonBox::Yes);
+ QPushButton * pbNo = dbbButtons->addButton(QDialogButtonBox::No);
+ QPushButton * pbMore = dbbButtons->addButton(QPushButton::tr("More info"), QDialogButtonBox::HelpRole);
+ layout->addWidget(dbbButtons);
+
+ connect(pbYes, SIGNAL(clicked()), this, SLOT(accept()));
+ connect(pbNo, SIGNAL(clicked()), this, SLOT(reject()));
+ connect(pbMore, SIGNAL(clicked()), this, SLOT(goToPageVideos()));
+
+ // update list periodically
+ QTimer * timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(updateList()));
+ timer->start(200);
+}
+
+void HWAskQuitDialog::goToPageVideos()
+{
+ reject();
+ form->GoToVideos();
+}
+
+void HWAskQuitDialog::updateList()
+{
+ QString text = form->ui.pageVideos->getVideosInProgress();
+ if (text.isEmpty())
+ {
+ // automatically exit when everything is finished
+ accept();
+ return;
+ }
+ lbList->setText(text);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/ask_quit.h Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,45 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef ASK_QUIT_H
+#define ASK_QUIT_H
+
+#include <QDialog>
+
+class QLabel;
+class HWForm;
+class PageVideos;
+
+class HWAskQuitDialog : public QDialog
+{
+ Q_OBJECT
+
+ public:
+ HWAskQuitDialog(QWidget* parent, HWForm *form);
+
+ private slots:
+ void goToPageVideos();
+ void updateList();
+
+ private:
+ HWForm * form;
+ QLabel * lbList;
+};
+
+
+#endif // INPUT_PASSWORD_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/upload_video.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,297 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QLineEdit>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QGridLayout>
+#include <QCheckBox>
+#include <QLabel>
+#include <QFrame>
+#include <QPlainTextEdit>
+#include <QSslError>
+#include <QUrl>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QMessageBox>
+#include <QRegExp>
+#include <QRegExpValidator>
+#include <QMessageBox>
+
+#include "upload_video.h"
+#include "hwconsts.h"
+
+// User-agent string used in http requests.
+// Don't make it a global varibale - crash on linux because of cVersionString
+#define USER_AGENT ("Hedgewars-QtFrontend/" + *cVersionString).toAscii()
+
+// This is developer key obtained from http://code.google.com/apis/youtube/dashboard/
+// If you are reusing this code outside Hedgewars, don't use this developer key,
+// obtain you own at http://code.google.com/apis/youtube/dashboard/
+static const QByteArray devKey = "AI39si5pKjxR0XgNIlmrEFF-LyYD31rps4g2O5dZTxLgD0fvJ2rHxrMrNFY8FYTZrzeI3VlaFVQLKfFnSBugvdZmy8vFzRDefQ";
+
+HWUploadVideoDialog::HWUploadVideoDialog(QWidget* parent, const QString &filename, QNetworkAccessManager* netManager) : QDialog(parent)
+{
+ this->filename = filename;
+ this->netManager = netManager;
+
+ setWindowTitle(tr("Upload video"));
+
+ // Google requires us to display this, see https://developers.google.com/youtube/terms
+ QString GoogleNotice =
+ "<p>By clicking 'upload,' you certify that you own all rights to the content or that "
+ "you are authorized by the owner to make the content publicly available on YouTube, "
+ "and that it otherwise complies with the YouTube Terms of Service located at "
+ "<a href=\"http://www.youtube.com/t/terms\" style=\"color: white;\">http://www.youtube.com/t/terms</a>.</p>";
+
+ // youtube doesn't understand this characters, even when they are properly escaped
+ // (either with CDATA or with < or >)
+ QRegExp rx("[^<>]*");
+
+ int row = 0;
+
+ QGridLayout * layout = new QGridLayout(this);
+ layout->setColumnStretch(0, 1);
+ layout->setColumnStretch(1, 2);
+
+ QLabel * lbLabel = new QLabel(this);
+ lbLabel->setWordWrap(true);
+ lbLabel->setText(QLabel::tr(
+ "Please provide either the YouTube account name "
+ "or the email address associated with the Google Account."));
+ layout->addWidget(lbLabel, row++, 0, 1, 2);
+
+ lbLabel = new QLabel(this);
+ lbLabel->setText(QLabel::tr("Account name (or email): "));
+ layout->addWidget(lbLabel, row, 0);
+
+ leAccount = new QLineEdit(this);
+ layout->addWidget(leAccount, row++, 1);
+
+ lbLabel = new QLabel(this);
+ lbLabel->setText(QLabel::tr("Password: "));
+ layout->addWidget(lbLabel, row, 0);
+
+ lePassword = new QLineEdit(this);
+ lePassword->setEchoMode(QLineEdit::Password);
+ layout->addWidget(lePassword, row++, 1);
+
+ cbSave = new QCheckBox(this);
+ cbSave->setText(QCheckBox::tr("Save account name and password"));
+ layout->addWidget(cbSave, row++, 0, 1, 2);
+
+ QFrame * hr = new QFrame(this);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ layout->addWidget(hr, row++, 0, 1, 2);
+
+ lbLabel = new QLabel(this);
+ lbLabel->setText(QLabel::tr("Video title: "));
+ layout->addWidget(lbLabel, row, 0);
+
+ leTitle = new QLineEdit(this);
+ leTitle->setText(filename);
+ leTitle->setValidator(new QRegExpValidator(rx, leTitle));
+ layout->addWidget(leTitle, row++, 1);
+
+ lbLabel = new QLabel(this);
+ lbLabel->setText(QLabel::tr("Video description: "));
+ layout->addWidget(lbLabel, row++, 0, 1, 2);
+
+ teDescription = new QPlainTextEdit(this);
+ layout->addWidget(teDescription, row++, 0, 1, 2);
+
+ lbLabel = new QLabel(this);
+ lbLabel->setText(QLabel::tr("Tags (comma separated): "));
+ layout->addWidget(lbLabel, row, 0);
+
+ leTags = new QLineEdit(this);
+ leTags->setText("hedgewars");
+ leTags->setMaxLength(500);
+ leTags->setValidator(new QRegExpValidator(rx, leTags));
+ layout->addWidget(leTags, row++, 1);
+
+ cbPrivate = new QCheckBox(this);
+ cbPrivate->setText(QCheckBox::tr("Video is private"));
+ layout->addWidget(cbPrivate, row++, 0, 1, 2);
+
+ hr = new QFrame(this);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ layout->addWidget(hr, row++, 0, 1, 2);
+
+ lbLabel = new QLabel(this);
+ lbLabel->setWordWrap(true);
+ lbLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
+ lbLabel->setTextFormat(Qt::RichText);
+ lbLabel->setOpenExternalLinks(true);
+ lbLabel->setText(GoogleNotice);
+ layout->addWidget(lbLabel, row++, 0, 1, 2);
+
+ QDialogButtonBox* dbbButtons = new QDialogButtonBox(this);
+ btnUpload = dbbButtons->addButton(tr("Upload"), QDialogButtonBox::ActionRole);
+ QPushButton * pbCancel = dbbButtons->addButton(QDialogButtonBox::Cancel);
+ layout->addWidget(dbbButtons, row++, 0, 1, 2);
+
+ /* hr = new QFrame(this);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ layout->addWidget(hr, row++, 0, 1, 2);*/
+
+ connect(btnUpload, SIGNAL(clicked()), this, SLOT(upload()));
+ connect(pbCancel, SIGNAL(clicked()), this, SLOT(reject()));
+}
+
+void HWUploadVideoDialog::showEvent(QShowEvent * event)
+{
+ QDialog::showEvent(event);
+
+ // set width to the same value as height (otherwise dialog has too small width)
+ QSize s = size();
+ QPoint p = pos();
+ resize(s.height(), s.height());
+ move(p.x() - (s.height() - s.width())/2, p.y());
+}
+
+void HWUploadVideoDialog::setEditable(bool editable)
+{
+ leTitle->setEnabled(editable);
+ leAccount->setEnabled(editable);
+ lePassword->setEnabled(editable);
+ btnUpload->setEnabled(editable);
+}
+
+void HWUploadVideoDialog::upload()
+{
+ setEditable(false);
+
+ // Documentation is at https://developers.google.com/youtube/2.0/developers_guide_protocol_clientlogin#ClientLogin_Authentication
+ QNetworkRequest request;
+ request.setUrl(QUrl("https://www.google.com/accounts/ClientLogin"));
+ request.setRawHeader("User-Agent", USER_AGENT);
+ request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
+
+ QString account(QUrl::toPercentEncoding(leAccount->text()));
+ QString pass(QUrl::toPercentEncoding(lePassword->text()));
+ QByteArray data = QString("Email=%1&Passwd=%2&service=youtube&source=Hedgewars").arg(account).arg(pass).toAscii();
+
+ QNetworkReply *reply = netManager->post(request, data);
+ connect(reply, SIGNAL(finished()), this, SLOT(authFinished()));
+}
+
+static QString XmlEscape(const QString& str)
+{
+ QString str2 = str;
+ // youtube doesn't understand this characters, even when they are properly escaped
+ // (either with CDATA or with < >)
+ str2.replace('<', ' ').replace('>', ' ');
+ return "<![CDATA[" + str2.replace("]]>", "]]]]><![CDATA[>") + "]]>";
+}
+
+void HWUploadVideoDialog::authFinished()
+{
+ QNetworkReply *reply = (QNetworkReply*)sender();
+ reply->deleteLater();
+
+ int HttpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ QByteArray answer = reply->readAll();
+ QString authToken = "";
+ QList<QByteArray> lines = answer.split('\n');
+ foreach (const QByteArray& line, lines)
+ {
+ QString str(line);
+ if (!str.startsWith("Auth=", Qt::CaseInsensitive))
+ continue;
+ str.remove(0, 5);
+ authToken = str;
+ break;
+ }
+ if (authToken.isEmpty())
+ {
+ QString errorStr = QMessageBox::tr("Error while authenticating at google.com:\n");
+ if (HttpCode == 403)
+ errorStr += QMessageBox::tr("Login or password is incorrect");
+ else
+ errorStr += reply->errorString();
+ QMessageBox::warning(this, QMessageBox::tr("Error"), errorStr);
+ setEditable(true);
+ return;
+ }
+
+ QByteArray auth = ("GoogleLogin auth=" + authToken).toAscii();
+
+ // We have authenticated, now we can send metadata and start upload
+ // Documentation is here: https://developers.google.com/youtube/2.0/developers_guide_protocol_resumable_uploads#Resumable_uploads
+ QByteArray body =
+ "<?xml version=\"1.0\"?>"
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
+ "xmlns:media=\"http://search.yahoo.com/mrss/\" "
+ "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">"
+ "<media:group>"
+ // "<yt:incomplete/>"
+ "<media:category "
+ "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">Games"
+ "</media:category>"
+ "<media:title type=\"plain\">"
+ + XmlEscape(leTitle->text()).toUtf8() +
+ "</media:title>"
+ "<media:description type=\"plain\">"
+ + XmlEscape(teDescription->toPlainText()).toUtf8() +
+ "</media:description>"
+ "<media:keywords type=\"plain\">"
+ + XmlEscape(leTags->text()).toUtf8() +
+ "</media:keywords>"
+ + (cbPrivate->isChecked()? "<yt:private/>" : "") +
+ "</media:group>"
+ "</entry>";
+
+ QNetworkRequest request;
+ request.setUrl(QUrl("http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads"));
+ request.setRawHeader("User-Agent", USER_AGENT);
+ request.setRawHeader("Authorization", auth);
+ request.setRawHeader("GData-Version", "2");
+ request.setRawHeader("X-GData-Key", "key=" + devKey);
+ request.setRawHeader("Slug", filename.toUtf8());
+ request.setRawHeader("Content-Type", "application/atom+xml; charset=UTF-8");
+
+ reply = netManager->post(request, body);
+ connect(reply, SIGNAL(finished()), this, SLOT(startUpload()));
+}
+
+void HWUploadVideoDialog::startUpload()
+{
+ QNetworkReply *reply = (QNetworkReply*)sender();
+ reply->deleteLater();
+
+ location = QString::fromAscii(reply->rawHeader("Location"));
+ if (location.isEmpty())
+ {
+ QString errorStr = QMessageBox::tr("Error while sending metadata to youtube.com:\n");
+ errorStr += reply->errorString();
+ QMessageBox::warning(this, QMessageBox::tr("Error"), errorStr);
+ setEditable(true);
+ return;
+ }
+
+ accept();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/upload_video.h Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,65 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef UPLOAD_VIDEO_H
+#define UPLOAD_VIDEO_H
+
+#include <QDialog>
+
+class QLineEdit;
+class QCheckBox;
+class QPlainTextEdit;
+class QLabel;
+class QNetworkAccessManager;
+
+class HWUploadVideoDialog : public QDialog
+{
+ Q_OBJECT
+ public:
+ HWUploadVideoDialog(QWidget* parent, const QString& filename, QNetworkAccessManager* netManager);
+
+ QLineEdit* leAccount;
+ QLineEdit* lePassword;
+ QCheckBox* cbSave;
+
+ QLineEdit* leTitle;
+ QPlainTextEdit* teDescription;
+ QLineEdit* leTags;
+ QCheckBox* cbPrivate;
+
+ QPushButton* btnUpload;
+
+ QString location;
+
+ private:
+ QNetworkAccessManager* netManager;
+ QString filename;
+
+ void setEditable(bool editable);
+
+ protected:
+ // virtual from QWidget
+ void showEvent(QShowEvent * event);
+
+ private slots:
+ void upload();
+ void authFinished();
+ void startUpload();
+};
+
+#endif // UPLOAD_VIDEO_H
--- a/QTfrontend/ui/page/pageeditteam.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageeditteam.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -289,6 +289,9 @@
int idx = list.indexOf("cpu.png");
if (idx >= 0)
list.removeAt(idx);
+ idx = list.indexOf("cpu_plain.png");
+ if (idx >= 0)
+ list.removeAt(idx);
idx = list.indexOf("hedgewars.png");
if (idx >= 0)
list.removeAt(idx);
@@ -469,5 +472,4 @@
void PageEditTeam::saveTeam()
{
data().saveToFile();
- emit teamEdited();
}
--- a/QTfrontend/ui/page/pageeditteam.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageeditteam.h Thu Aug 30 13:02:19 2012 -0400
@@ -40,9 +40,6 @@
void editTeam(const QString & name, const QString & playerHash);
void deleteTeam(const QString & name);
- signals:
- void teamEdited();
-
public slots:
void CBFort_activated(const QString & gravename);
--- a/QTfrontend/ui/page/pagemain.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pagemain.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -85,9 +85,15 @@
bottomLayout->setStretch(0,1);
btnBack->setWhatsThis(tr("Exit game"));
-
- BtnSetup = addButton(":/res/Settings.png", bottomLayout, 1, true);
+
+#ifdef VIDEOREC
+ BtnVideos = addButton(":/res/Record.png", bottomLayout, 1, true);
+ BtnVideos->setWhatsThis(tr("Manage videos recorded from game"));
+#endif
+
+ BtnSetup = addButton(":/res/Settings.png", bottomLayout, 2, true);
BtnSetup->setWhatsThis(tr("Edit game preferences"));
+
return bottomLayout;
}
--- a/QTfrontend/ui/page/pagemain.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pagemain.h Thu Aug 30 13:02:19 2012 -0400
@@ -34,6 +34,7 @@
QPushButton * BtnFeedback;
QPushButton * BtnInfo;
QPushButton * BtnDataDownload;
+ QPushButton * BtnVideos;
QLabel * mainNote;
private:
--- a/QTfrontend/ui/page/pageoptions.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageoptions.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -28,368 +28,397 @@
#include <QTextBrowser>
#include <QTableWidget>
#include <QSlider>
+#include <QSignalMapper>
+#include <QColorDialog>
+#include <QStandardItemModel>
#include "pageoptions.h"
#include "hwconsts.h"
#include "fpsedit.h"
#include "igbox.h"
+#include "DataManager.h"
// TODO cleanup
QLayout * PageOptions::bodyLayoutDefinition()
{
- QGridLayout * pageLayout = new QGridLayout();
- pageLayout->setColumnStretch(0, 100);
- pageLayout->setColumnStretch(1, 100);
- pageLayout->setColumnStretch(2, 100);
- pageLayout->setRowStretch(0, 0);
- //pageLayout->setRowStretch(1, 100);
- pageLayout->setRowStretch(2, 0);
- pageLayout->setContentsMargins(7, 7, 7, 0);
- pageLayout->setSpacing(0);
+ QVBoxLayout * pageLayout = new QVBoxLayout();
+ QTabWidget * tabs = new QTabWidget(this);
+ pageLayout->addWidget(tabs);
+ QWidget * page1 = new QWidget(this);
+ QWidget * page2 = new QWidget(this);
+ tabs->addTab(page1, tr("General"));
+ tabs->addTab(page2, tr("Advanced"));
+
+ { // page 1
+ QGridLayout * page1Layout = new QGridLayout(page1);
+ //gbTBLayout->setMargin(0);
+ page1Layout->setSpacing(0);
+ page1Layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
- QGroupBox * gbTwoBoxes = new QGroupBox(this);
- pageLayout->addWidget(gbTwoBoxes, 0, 0, 1, 3);
- QGridLayout * gbTBLayout = new QGridLayout(gbTwoBoxes);
- gbTBLayout->setMargin(0);
- gbTBLayout->setSpacing(0);
- gbTBLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
-
- QPixmap pmNew(":/res/new.png");
- QPixmap pmEdit(":/res/edit.png");
- QPixmap pmDelete(":/res/delete.png");
+ QPixmap pmNew(":/res/new.png");
+ QPixmap pmEdit(":/res/edit.png");
+ QPixmap pmDelete(":/res/delete.png");
- {
- teamsBox = new IconedGroupBox(this);
- //teamsBox->setContentTopPadding(0);
- //teamsBox->setAttribute(Qt::WA_PaintOnScreen, true);
- teamsBox->setIcon(QIcon(":/res/teamicon.png"));
- teamsBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- teamsBox->setTitle(QGroupBox::tr("Teams"));
+ {
+ teamsBox = new IconedGroupBox(this);
+ //teamsBox->setContentTopPadding(0);
+ //teamsBox->setAttribute(Qt::WA_PaintOnScreen, true);
+ teamsBox->setIcon(QIcon(":/res/teamicon.png"));
+ teamsBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ teamsBox->setTitle(QGroupBox::tr("Teams"));
- QGridLayout * GBTlayout = new QGridLayout(teamsBox);
+ QGridLayout * GBTlayout = new QGridLayout(teamsBox);
- CBTeamName = new QComboBox(teamsBox);
- GBTlayout->addWidget(CBTeamName, 0, 0);
+ CBTeamName = new QComboBox(teamsBox);
+ GBTlayout->addWidget(CBTeamName, 0, 0);
- BtnNewTeam = new QPushButton(teamsBox);
- BtnNewTeam->setToolTip(tr("New team"));
- BtnNewTeam->setIconSize(pmNew.size());
- BtnNewTeam->setIcon(pmNew);
- BtnNewTeam->setMaximumWidth(pmNew.width() + 6);
- connect(BtnNewTeam, SIGNAL(clicked()), this, SIGNAL(newTeamRequested()));
- GBTlayout->addWidget(BtnNewTeam, 0, 1);
+ BtnNewTeam = new QPushButton(teamsBox);
+ BtnNewTeam->setToolTip(tr("New team"));
+ BtnNewTeam->setIconSize(pmNew.size());
+ BtnNewTeam->setIcon(pmNew);
+ BtnNewTeam->setMaximumWidth(pmNew.width() + 6);
+ connect(BtnNewTeam, SIGNAL(clicked()), this, SIGNAL(newTeamRequested()));
+ GBTlayout->addWidget(BtnNewTeam, 0, 1);
- BtnEditTeam = new QPushButton(teamsBox);
- BtnEditTeam->setToolTip(tr("Edit team"));
- BtnEditTeam->setIconSize(pmEdit.size());
- BtnEditTeam->setIcon(pmEdit);
- BtnEditTeam->setMaximumWidth(pmEdit.width() + 6);
- connect(BtnEditTeam, SIGNAL(clicked()), this, SLOT(requestEditSelectedTeam()));
- GBTlayout->addWidget(BtnEditTeam, 0, 2);
+ BtnEditTeam = new QPushButton(teamsBox);
+ BtnEditTeam->setToolTip(tr("Edit team"));
+ BtnEditTeam->setIconSize(pmEdit.size());
+ BtnEditTeam->setIcon(pmEdit);
+ BtnEditTeam->setMaximumWidth(pmEdit.width() + 6);
+ connect(BtnEditTeam, SIGNAL(clicked()), this, SLOT(requestEditSelectedTeam()));
+ GBTlayout->addWidget(BtnEditTeam, 0, 2);
- BtnDeleteTeam = new QPushButton(teamsBox);
- BtnDeleteTeam->setToolTip(tr("Delete team"));
- BtnDeleteTeam->setIconSize(pmDelete.size());
- BtnDeleteTeam->setIcon(pmDelete);
- BtnDeleteTeam->setMaximumWidth(pmDelete.width() + 6);
- connect(BtnDeleteTeam, SIGNAL(clicked()), this, SLOT(requestDeleteSelectedTeam()));
- GBTlayout->addWidget(BtnDeleteTeam, 0, 3);
-
- LblNoEditTeam = new QLabel(teamsBox);
- LblNoEditTeam->setText(tr("You can't edit teams from team selection. Go back to main menu to add, edit or delete teams."));
- LblNoEditTeam->setWordWrap(true);
- LblNoEditTeam->setVisible(false);
- GBTlayout->addWidget(LblNoEditTeam, 0, 0);
+ BtnDeleteTeam = new QPushButton(teamsBox);
+ BtnDeleteTeam->setToolTip(tr("Delete team"));
+ BtnDeleteTeam->setIconSize(pmDelete.size());
+ BtnDeleteTeam->setIcon(pmDelete);
+ BtnDeleteTeam->setMaximumWidth(pmDelete.width() + 6);
+ connect(BtnDeleteTeam, SIGNAL(clicked()), this, SLOT(requestDeleteSelectedTeam()));
+ GBTlayout->addWidget(BtnDeleteTeam, 0, 3);
- gbTBLayout->addWidget(teamsBox, 0, 0);
- }
-
- {
- IconedGroupBox* groupWeapons = new IconedGroupBox(this);
+ LblNoEditTeam = new QLabel(teamsBox);
+ LblNoEditTeam->setText(tr("You can't edit teams from team selection. Go back to main menu to add, edit or delete teams."));
+ LblNoEditTeam->setWordWrap(true);
+ LblNoEditTeam->setVisible(false);
+ GBTlayout->addWidget(LblNoEditTeam, 0, 0);
- //groupWeapons->setContentTopPadding(0);
- //groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- groupWeapons->setIcon(QIcon(":/res/weaponsicon.png"));
- groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- groupWeapons->setTitle(QGroupBox::tr("Schemes and Weapons"));
- QGridLayout * WeaponsLayout = new QGridLayout(groupWeapons);
+ page1Layout->addWidget(teamsBox, 0, 0);
+ }
- QLabel* SchemeLabel = new QLabel(groupWeapons);
- SchemeLabel->setText(QLabel::tr("Game scheme"));
- WeaponsLayout->addWidget(SchemeLabel, 1, 0);
+ {
+ IconedGroupBox* groupWeapons = new IconedGroupBox(this);
- SchemesName = new QComboBox(groupWeapons);
- WeaponsLayout->addWidget(SchemesName, 1, 1);
+ //groupWeapons->setContentTopPadding(0);
+ //groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ groupWeapons->setIcon(QIcon(":/res/weaponsicon.png"));
+ groupWeapons->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ groupWeapons->setTitle(QGroupBox::tr("Schemes and Weapons"));
+ QGridLayout * WeaponsLayout = new QGridLayout(groupWeapons);
- SchemeNew = new QPushButton(groupWeapons);
- SchemeNew->setWhatsThis(tr("New scheme"));
- SchemeNew->setIconSize(pmNew.size());
- SchemeNew->setIcon(pmNew);
- SchemeNew->setMaximumWidth(pmNew.width() + 6);
- WeaponsLayout->addWidget(SchemeNew, 1, 2);
+ QLabel* SchemeLabel = new QLabel(groupWeapons);
+ SchemeLabel->setText(QLabel::tr("Game scheme"));
+ WeaponsLayout->addWidget(SchemeLabel, 1, 0);
- SchemeEdit = new QPushButton(groupWeapons);
- SchemeEdit->setWhatsThis(tr("Edit scheme"));
- SchemeEdit->setIconSize(pmEdit.size());
- SchemeEdit->setIcon(pmEdit);
- SchemeEdit->setMaximumWidth(pmEdit.width() + 6);
- WeaponsLayout->addWidget(SchemeEdit, 1, 3);
+ SchemesName = new QComboBox(groupWeapons);
+ WeaponsLayout->addWidget(SchemesName, 1, 1);
- SchemeDelete = new QPushButton(groupWeapons);
- SchemeDelete->setWhatsThis(tr("Delete scheme"));
- SchemeDelete->setIconSize(pmDelete.size());
- SchemeDelete->setIcon(pmDelete);
- SchemeDelete->setMaximumWidth(pmDelete.width() + 6);
- WeaponsLayout->addWidget(SchemeDelete, 1, 4);
+ SchemeNew = new QPushButton(groupWeapons);
+ SchemeNew->setWhatsThis(tr("New scheme"));
+ SchemeNew->setIconSize(pmNew.size());
+ SchemeNew->setIcon(pmNew);
+ SchemeNew->setMaximumWidth(pmNew.width() + 6);
+ WeaponsLayout->addWidget(SchemeNew, 1, 2);
- QLabel* WeaponLabel = new QLabel(groupWeapons);
- WeaponLabel->setText(QLabel::tr("Weapons"));
- WeaponsLayout->addWidget(WeaponLabel, 2, 0);
-
- WeaponsName = new QComboBox(groupWeapons);
- WeaponsLayout->addWidget(WeaponsName, 2, 1);
-
- WeaponNew = new QPushButton(groupWeapons);
- WeaponNew->setWhatsThis(tr("New weapon set"));
- WeaponNew->setIconSize(pmNew.size());
- WeaponNew->setIcon(pmNew);
- WeaponNew->setMaximumWidth(pmNew.width() + 6);
- WeaponsLayout->addWidget(WeaponNew, 2, 2);
+ SchemeEdit = new QPushButton(groupWeapons);
+ SchemeEdit->setWhatsThis(tr("Edit scheme"));
+ SchemeEdit->setIconSize(pmEdit.size());
+ SchemeEdit->setIcon(pmEdit);
+ SchemeEdit->setMaximumWidth(pmEdit.width() + 6);
+ WeaponsLayout->addWidget(SchemeEdit, 1, 3);
- WeaponEdit = new QPushButton(groupWeapons);
- WeaponEdit->setWhatsThis(tr("Edit weapon set"));
- WeaponEdit->setIconSize(pmEdit.size());
- WeaponEdit->setIcon(pmEdit);
- WeaponEdit->setMaximumWidth(pmEdit.width() + 6);
- WeaponsLayout->addWidget(WeaponEdit, 2, 3);
+ SchemeDelete = new QPushButton(groupWeapons);
+ SchemeDelete->setWhatsThis(tr("Delete scheme"));
+ SchemeDelete->setIconSize(pmDelete.size());
+ SchemeDelete->setIcon(pmDelete);
+ SchemeDelete->setMaximumWidth(pmDelete.width() + 6);
+ WeaponsLayout->addWidget(SchemeDelete, 1, 4);
- WeaponDelete = new QPushButton(groupWeapons);
- WeaponDelete->setWhatsThis(tr("Delete weapon set"));
- WeaponDelete->setIconSize(pmDelete.size());
- WeaponDelete->setIcon(pmDelete);
- WeaponDelete->setMaximumWidth(pmDelete.width() + 6);
- WeaponsLayout->addWidget(WeaponDelete, 2, 4);
+ QLabel* WeaponLabel = new QLabel(groupWeapons);
+ WeaponLabel->setText(QLabel::tr("Weapons"));
+ WeaponsLayout->addWidget(WeaponLabel, 2, 0);
- WeaponTooltip = new QCheckBox(this);
- WeaponTooltip->setText(QCheckBox::tr("Show ammo menu tooltips"));
- WeaponsLayout->addWidget(WeaponTooltip, 3, 0, 1, 4);
-
- gbTBLayout->addWidget(groupWeapons, 1, 0);
- }
+ WeaponsName = new QComboBox(groupWeapons);
+ WeaponsLayout->addWidget(WeaponsName, 2, 1);
- {
- IconedGroupBox* groupMisc = new IconedGroupBox(this);
- //groupMisc->setContentTopPadding(0);
- groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
- groupMisc->setIcon(QIcon(":/res/miscicon.png"));
- //groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- groupMisc->setTitle(QGroupBox::tr("Misc"));
- QGridLayout * MiscLayout = new QGridLayout(groupMisc);
+ WeaponNew = new QPushButton(groupWeapons);
+ WeaponNew->setWhatsThis(tr("New weapon set"));
+ WeaponNew->setIconSize(pmNew.size());
+ WeaponNew->setIcon(pmNew);
+ WeaponNew->setMaximumWidth(pmNew.width() + 6);
+ WeaponsLayout->addWidget(WeaponNew, 2, 2);
- // Label for "Language"
- QLabel *labelLanguage = new QLabel(groupMisc);
- labelLanguage->setText(QLabel::tr("Locale") + " *");
- MiscLayout->addWidget(labelLanguage, 0, 0);
+ WeaponEdit = new QPushButton(groupWeapons);
+ WeaponEdit->setWhatsThis(tr("Edit weapon set"));
+ WeaponEdit->setIconSize(pmEdit.size());
+ WeaponEdit->setIcon(pmEdit);
+ WeaponEdit->setMaximumWidth(pmEdit.width() + 6);
+ WeaponsLayout->addWidget(WeaponEdit, 2, 3);
- // List of installed languages
- CBLanguage = new QComboBox(groupMisc);
- QDir tmpdir;
- tmpdir.cd(cfgdir->absolutePath());
- tmpdir.cd("Data/Locale");
- tmpdir.setFilter(QDir::Files);
- QStringList locs = tmpdir.entryList(QStringList("hedgewars_*.qm"));
- CBLanguage->addItem(QComboBox::tr("(System default)"), QString(""));
- for(int i = 0; i < locs.count(); i++)
- {
- QLocale loc(locs[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
- CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+ WeaponDelete = new QPushButton(groupWeapons);
+ WeaponDelete->setWhatsThis(tr("Delete weapon set"));
+ WeaponDelete->setIconSize(pmDelete.size());
+ WeaponDelete->setIcon(pmDelete);
+ WeaponDelete->setMaximumWidth(pmDelete.width() + 6);
+ WeaponsLayout->addWidget(WeaponDelete, 2, 4);
+
+ WeaponTooltip = new QCheckBox(this);
+ WeaponTooltip->setText(QCheckBox::tr("Show ammo menu tooltips"));
+ WeaponsLayout->addWidget(WeaponTooltip, 3, 0, 1, 4);
+
+ page1Layout->addWidget(groupWeapons, 1, 0);
}
- tmpdir.cd(datadir->absolutePath());
- tmpdir.cd("Locale");
- tmpdir.setFilter(QDir::Files);
- QStringList tmplist = tmpdir.entryList(QStringList("hedgewars_*.qm"));
- for(int i = 0; i < tmplist.count(); i++)
{
- if (locs.contains(tmplist[i])) continue;
- QLocale loc(tmplist[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
- CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+ IconedGroupBox* groupMisc = new IconedGroupBox(this);
+ //groupMisc->setContentTopPadding(0);
+ groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
+ groupMisc->setIcon(QIcon(":/res/miscicon.png"));
+ //groupMisc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ groupMisc->setTitle(QGroupBox::tr("Misc"));
+ QGridLayout * MiscLayout = new QGridLayout(groupMisc);
+
+ // Label for "Language"
+ QLabel *labelLanguage = new QLabel(groupMisc);
+ labelLanguage->setText(QLabel::tr("Locale") + " *");
+ MiscLayout->addWidget(labelLanguage, 0, 0);
+
+ // List of installed languages
+ CBLanguage = new QComboBox(groupMisc);
+ QDir tmpdir;
+ tmpdir.cd(cfgdir->absolutePath());
+ tmpdir.cd("Data/Locale");
+ tmpdir.setFilter(QDir::Files);
+ QStringList locs = tmpdir.entryList(QStringList("hedgewars_*.qm"));
+ CBLanguage->addItem(QComboBox::tr("(System default)"), QString(""));
+ for(int i = 0; i < locs.count(); i++)
+ {
+ QLocale loc(locs[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
+ CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+ }
+
+ tmpdir.cd(datadir->absolutePath());
+ tmpdir.cd("Locale");
+ tmpdir.setFilter(QDir::Files);
+ QStringList tmplist = tmpdir.entryList(QStringList("hedgewars_*.qm"));
+ for(int i = 0; i < tmplist.count(); i++)
+ {
+ if (locs.contains(tmplist[i])) continue;
+ QLocale loc(tmplist[i].replace(QRegExp("hedgewars_(.*)\\.qm"), "\\1"));
+ CBLanguage->addItem(QLocale::languageToString(loc.language()) + " (" + QLocale::countryToString(loc.country()) + ")", loc.name());
+ }
+
+ MiscLayout->addWidget(CBLanguage, 0, 1);
+
+ // Label and field for net nick
+ labelNN = new QLabel(groupMisc);
+ labelNN->setText(QLabel::tr("Nickname"));
+ MiscLayout->addWidget(labelNN, 1, 0);
+
+ editNetNick = new QLineEdit(groupMisc);
+ editNetNick->setMaxLength(20);
+ editNetNick->setText(QLineEdit::tr("anonymous"));
+ MiscLayout->addWidget(editNetNick, 1, 1);
+
+ // checkbox and field for password
+ CBSavePassword = new QCheckBox(groupMisc);
+ CBSavePassword->setText(QCheckBox::tr("Save password"));
+ MiscLayout->addWidget(CBSavePassword, 2, 0);
+
+ editNetPassword = new QLineEdit(groupMisc);
+ editNetPassword->setEchoMode(QLineEdit::Password);
+ MiscLayout->addWidget(editNetPassword, 2, 1);
+
+ CBNameWithDate = new QCheckBox(groupMisc);
+ CBNameWithDate->setText(QCheckBox::tr("Append date and time to record file name"));
+ MiscLayout->addWidget(CBNameWithDate, 5, 0, 1, 2);
+
+ BtnAssociateFiles = new QPushButton(groupMisc);
+ BtnAssociateFiles->setText(QPushButton::tr("Associate file extensions"));
+ BtnAssociateFiles->setVisible(!custom_data && !custom_config);
+ MiscLayout->addWidget(BtnAssociateFiles, 6, 0, 1, 2);
+
+ #ifdef __APPLE__
+ #ifdef SPARKLE_ENABLED
+ CBAutoUpdate = new QCheckBox(groupMisc);
+ CBAutoUpdate->setText(QCheckBox::tr("Check for updates at startup"));
+ MiscLayout->addWidget(CBAutoUpdate, 7, 0, 1, 3);
+ #endif
+ #endif
+ page1Layout->addWidget(groupMisc, 2, 0);
}
- MiscLayout->addWidget(CBLanguage, 0, 1);
+ {
+ AGGroupBox = new IconedGroupBox(this);
+ //AGGroupBox->setContentTopPadding(0);
+ AGGroupBox->setIcon(QIcon(":/res/graphicsicon.png"));
+ //AGGroupBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ AGGroupBox->setTitle(QGroupBox::tr("Audio/Graphic options"));
+
+ QVBoxLayout * GBAlayout = new QVBoxLayout(AGGroupBox);
+ QHBoxLayout * GBAreslayout = new QHBoxLayout(0);
+ QHBoxLayout * GBAstereolayout = new QHBoxLayout(0);
+ QHBoxLayout * GBAqualayout = new QHBoxLayout(0);
- // Label and field for net nick
- labelNN = new QLabel(groupMisc);
- labelNN->setText(QLabel::tr("Nickname"));
- MiscLayout->addWidget(labelNN, 1, 0);
+ CBFrontendFullscreen = new QCheckBox(AGGroupBox);
+ CBFrontendFullscreen->setText(QCheckBox::tr("Frontend fullscreen"));
+ GBAlayout->addWidget(CBFrontendFullscreen);
+
+ CBFrontendEffects = new QCheckBox(AGGroupBox);
+ CBFrontendEffects->setText(QCheckBox::tr("Frontend effects"));
+ GBAlayout->addWidget(CBFrontendEffects);
+
+ CBEnableFrontendSound = new QCheckBox(AGGroupBox);
+ CBEnableFrontendSound->setText(QCheckBox::tr("Enable frontend sounds"));
+ GBAlayout->addWidget(CBEnableFrontendSound);
+
+ CBEnableFrontendMusic = new QCheckBox(AGGroupBox);
+ CBEnableFrontendMusic->setText(QCheckBox::tr("Enable frontend music"));
+ GBAlayout->addWidget(CBEnableFrontendMusic);
- editNetNick = new QLineEdit(groupMisc);
- editNetNick->setMaxLength(20);
- editNetNick->setText(QLineEdit::tr("anonymous"));
- MiscLayout->addWidget(editNetNick, 1, 1);
+ QFrame * hr = new QFrame(AGGroupBox);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ GBAlayout->addWidget(hr);
+
+ QLabel * resolution = new QLabel(AGGroupBox);
+ resolution->setText(QLabel::tr("Resolution"));
+ GBAreslayout->addWidget(resolution);
+
+ CBResolution = new QComboBox(AGGroupBox);
+ GBAreslayout->addWidget(CBResolution);
+ GBAlayout->addLayout(GBAreslayout);
- // checkbox and field for password
- CBSavePassword = new QCheckBox(groupMisc);
- CBSavePassword->setText(QCheckBox::tr("Save password"));
- MiscLayout->addWidget(CBSavePassword, 2, 0);
+ CBFullscreen = new QCheckBox(AGGroupBox);
+ CBFullscreen->setText(QCheckBox::tr("Fullscreen"));
+ GBAreslayout->addWidget(CBFullscreen);
+
+ QLabel * quality = new QLabel(AGGroupBox);
+ quality->setText(QLabel::tr("Quality"));
+ quality->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ GBAqualayout->addWidget(quality);
+
+ SLQuality = new QSlider(Qt::Horizontal, AGGroupBox);
+ SLQuality->setTickPosition(QSlider::TicksBelow);
+ SLQuality->setMaximum(5);
+ SLQuality->setMinimum(0);
+ SLQuality->setFixedWidth(150);
+ GBAqualayout->addWidget(SLQuality);
+ GBAlayout->addLayout(GBAqualayout);
+
+ QLabel * stereo = new QLabel(AGGroupBox);
+ stereo->setText(QLabel::tr("Stereo rendering"));
+ GBAstereolayout->addWidget(stereo);
- editNetPassword = new QLineEdit(groupMisc);
- editNetPassword->setEchoMode(QLineEdit::Password);
- MiscLayout->addWidget(editNetPassword, 2, 1);
+ CBStereoMode = new QComboBox(AGGroupBox);
+ CBStereoMode->addItem(QComboBox::tr("Disabled"));
+ CBStereoMode->addItem(QComboBox::tr("Red/Cyan"));
+ CBStereoMode->addItem(QComboBox::tr("Cyan/Red"));
+ CBStereoMode->addItem(QComboBox::tr("Red/Blue"));
+ CBStereoMode->addItem(QComboBox::tr("Blue/Red"));
+ CBStereoMode->addItem(QComboBox::tr("Red/Green"));
+ CBStereoMode->addItem(QComboBox::tr("Green/Red"));
+ CBStereoMode->addItem(QComboBox::tr("Side-by-side"));
+ CBStereoMode->addItem(QComboBox::tr("Top-Bottom"));
+ CBStereoMode->addItem(QComboBox::tr("Wiggle"));
+ CBStereoMode->addItem(QComboBox::tr("Red/Cyan grayscale"));
+ CBStereoMode->addItem(QComboBox::tr("Cyan/Red grayscale"));
+ CBStereoMode->addItem(QComboBox::tr("Red/Blue grayscale"));
+ CBStereoMode->addItem(QComboBox::tr("Blue/Red grayscale"));
+ CBStereoMode->addItem(QComboBox::tr("Red/Green grayscale"));
+ CBStereoMode->addItem(QComboBox::tr("Green/Red grayscale"));
- CBNameWithDate = new QCheckBox(groupMisc);
- CBNameWithDate->setText(QCheckBox::tr("Append date and time to record file name"));
- MiscLayout->addWidget(CBNameWithDate, 5, 0, 1, 2);
+ GBAstereolayout->addWidget(CBStereoMode);
+ GBAlayout->addLayout(GBAstereolayout);
+
+ QHBoxLayout * GBAfpslayout = new QHBoxLayout(0);
+ QLabel * maxfps = new QLabel(AGGroupBox);
+ maxfps->setText(QLabel::tr("FPS limit"));
+ GBAfpslayout->addWidget(maxfps);
+ GBAlayout->addLayout(GBAfpslayout);
+ fpsedit = new FPSEdit(AGGroupBox);
+ GBAfpslayout->addWidget(fpsedit);
+
+ CBShowFPS = new QCheckBox(AGGroupBox);
+ CBShowFPS->setText(QCheckBox::tr("Show FPS"));
+ GBAfpslayout->addWidget(CBShowFPS);
- BtnAssociateFiles = new QPushButton(groupMisc);
- BtnAssociateFiles->setText(QPushButton::tr("Associate file extensions"));
- BtnAssociateFiles->setVisible(!custom_data && !custom_config);
- MiscLayout->addWidget(BtnAssociateFiles, 6, 0, 1, 2);
+ hr = new QFrame(AGGroupBox);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ GBAlayout->addWidget(hr);
+
+ QGridLayout * GBAvollayout = new QGridLayout();
+ QLabel * vol = new QLabel(AGGroupBox);
+ vol->setText(QLabel::tr("Initial sound volume"));
+ GBAvollayout->addWidget(vol, 0, 0, 1, 2);
+ GBAlayout->addLayout(GBAvollayout);
+ volumeBox = new QSpinBox(AGGroupBox);
+ volumeBox->setRange(0, 100);
+ volumeBox->setSingleStep(5);
+ GBAvollayout->addWidget(volumeBox, 0, 2);
-#ifdef __APPLE__
-#ifdef SPARKLE_ENABLED
- CBAutoUpdate = new QCheckBox(groupMisc);
- CBAutoUpdate->setText(QCheckBox::tr("Check for updates at startup"));
- MiscLayout->addWidget(CBAutoUpdate, 7, 0, 1, 3);
-#endif
-#endif
- gbTBLayout->addWidget(groupMisc, 2, 0);
+ CBEnableSound = new QCheckBox(AGGroupBox);
+ CBEnableSound->setText(QCheckBox::tr("Enable sound"));
+ GBAvollayout->addWidget(CBEnableSound, 1, 0, 1, 1);
+
+ CBEnableMusic = new QCheckBox(AGGroupBox);
+ CBEnableMusic->setText(QCheckBox::tr("Enable music"));
+ GBAvollayout->addWidget(CBEnableMusic, 1, 1, 1, 2);
+
+ GBAvollayout->setSizeConstraint(QLayout::SetMinimumSize);
+
+ hr = new QFrame(AGGroupBox);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ GBAlayout->addWidget(hr);
+
+ CBAltDamage = new QCheckBox(AGGroupBox);
+ CBAltDamage->setText(QCheckBox::tr("Alternative damage show"));
+ GBAlayout->addWidget(CBAltDamage);
+
+ page1Layout->addWidget(AGGroupBox, 0, 1, 3, 1);
+ }
}
- {
- AGGroupBox = new IconedGroupBox(this);
- //AGGroupBox->setContentTopPadding(0);
- AGGroupBox->setIcon(QIcon(":/res/graphicsicon.png"));
- //AGGroupBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
- AGGroupBox->setTitle(QGroupBox::tr("Audio/Graphic options"));
-
- QVBoxLayout * GBAlayout = new QVBoxLayout(AGGroupBox);
- QHBoxLayout * GBAreslayout = new QHBoxLayout(0);
- QHBoxLayout * GBAstereolayout = new QHBoxLayout(0);
- QHBoxLayout * GBAqualayout = new QHBoxLayout(0);
-
- CBFrontendFullscreen = new QCheckBox(AGGroupBox);
- CBFrontendFullscreen->setText(QCheckBox::tr("Frontend fullscreen"));
- GBAlayout->addWidget(CBFrontendFullscreen);
-
- CBFrontendEffects = new QCheckBox(AGGroupBox);
- CBFrontendEffects->setText(QCheckBox::tr("Frontend effects"));
- GBAlayout->addWidget(CBFrontendEffects);
-
- CBEnableFrontendSound = new QCheckBox(AGGroupBox);
- CBEnableFrontendSound->setText(QCheckBox::tr("Enable frontend sounds"));
- GBAlayout->addWidget(CBEnableFrontendSound);
-
- CBEnableFrontendMusic = new QCheckBox(AGGroupBox);
- CBEnableFrontendMusic->setText(QCheckBox::tr("Enable frontend music"));
- GBAlayout->addWidget(CBEnableFrontendMusic);
+ { // page 2
+ QGridLayout * page2Layout = new QGridLayout(page2);
- QFrame * hr = new QFrame(AGGroupBox);
- hr->setFrameStyle(QFrame::HLine);
- hr->setLineWidth(3);
- hr->setFixedHeight(10);
- GBAlayout->addWidget(hr);
-
- QLabel * resolution = new QLabel(AGGroupBox);
- resolution->setText(QLabel::tr("Resolution"));
- GBAreslayout->addWidget(resolution);
-
- CBResolution = new QComboBox(AGGroupBox);
- GBAreslayout->addWidget(CBResolution);
- GBAlayout->addLayout(GBAreslayout);
+ IconedGroupBox * gbColors = new IconedGroupBox(this);
+ //gbColors->setIcon(QIcon(":/res/teamicon.png"));
+ gbColors->setTitle(QGroupBox::tr("Custom colors"));
+ page2Layout->addWidget(gbColors, 0, 0, 1, 3);
+ QVBoxLayout * gbCLayout = new QVBoxLayout(gbColors);
- CBFullscreen = new QCheckBox(AGGroupBox);
- CBFullscreen->setText(QCheckBox::tr("Fullscreen"));
- GBAreslayout->addWidget(CBFullscreen);
-
- QLabel * quality = new QLabel(AGGroupBox);
- quality->setText(QLabel::tr("Quality"));
- quality->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- GBAqualayout->addWidget(quality);
+ QSignalMapper * mapper = new QSignalMapper(this);
- SLQuality = new QSlider(Qt::Horizontal, AGGroupBox);
- SLQuality->setTickPosition(QSlider::TicksBelow);
- SLQuality->setMaximum(5);
- SLQuality->setMinimum(0);
- SLQuality->setFixedWidth(150);
- GBAqualayout->addWidget(SLQuality);
- GBAlayout->addLayout(GBAqualayout);
-
- QLabel * stereo = new QLabel(AGGroupBox);
- stereo->setText(QLabel::tr("Stereo rendering"));
- GBAstereolayout->addWidget(stereo);
+ QStandardItemModel * model = DataManager::instance().colorsModel();
- CBStereoMode = new QComboBox(AGGroupBox);
- CBStereoMode->addItem(QComboBox::tr("Disabled"));
- CBStereoMode->addItem(QComboBox::tr("Red/Cyan"));
- CBStereoMode->addItem(QComboBox::tr("Cyan/Red"));
- CBStereoMode->addItem(QComboBox::tr("Red/Blue"));
- CBStereoMode->addItem(QComboBox::tr("Blue/Red"));
- CBStereoMode->addItem(QComboBox::tr("Red/Green"));
- CBStereoMode->addItem(QComboBox::tr("Green/Red"));
- CBStereoMode->addItem(QComboBox::tr("Side-by-side"));
- CBStereoMode->addItem(QComboBox::tr("Top-Bottom"));
- CBStereoMode->addItem(QComboBox::tr("Wiggle"));
- CBStereoMode->addItem(QComboBox::tr("Red/Cyan grayscale"));
- CBStereoMode->addItem(QComboBox::tr("Cyan/Red grayscale"));
- CBStereoMode->addItem(QComboBox::tr("Red/Blue grayscale"));
- CBStereoMode->addItem(QComboBox::tr("Blue/Red grayscale"));
- CBStereoMode->addItem(QComboBox::tr("Red/Green grayscale"));
- CBStereoMode->addItem(QComboBox::tr("Green/Red grayscale"));
-
- GBAstereolayout->addWidget(CBStereoMode);
- GBAlayout->addLayout(GBAstereolayout);
-
- QHBoxLayout * GBAfpslayout = new QHBoxLayout(0);
- QLabel * maxfps = new QLabel(AGGroupBox);
- maxfps->setText(QLabel::tr("FPS limit"));
- GBAfpslayout->addWidget(maxfps);
- GBAlayout->addLayout(GBAfpslayout);
- fpsedit = new FPSEdit(AGGroupBox);
- GBAfpslayout->addWidget(fpsedit);
-
- CBShowFPS = new QCheckBox(AGGroupBox);
- CBShowFPS->setText(QCheckBox::tr("Show FPS"));
- GBAfpslayout->addWidget(CBShowFPS);
+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(onColorModelDataChanged(QModelIndex,QModelIndex)));
+ for(int i = 0; i < model->rowCount(); ++i)
+ {
+ QPushButton * btn = new QPushButton(this);
+ gbCLayout->addWidget(btn);
+ btn->setStyleSheet(QString("background: %1").arg(model->item(i)->data().value<QColor>().name()));
+ m_colorButtons.append(btn);
+ connect(btn, SIGNAL(clicked()), mapper, SLOT(map()));
+ mapper->setMapping(btn, i);
+ }
- hr = new QFrame(AGGroupBox);
- hr->setFrameStyle(QFrame::HLine);
- hr->setLineWidth(3);
- hr->setFixedHeight(10);
- GBAlayout->addWidget(hr);
-
- QGridLayout * GBAvollayout = new QGridLayout();
- QLabel * vol = new QLabel(AGGroupBox);
- vol->setText(QLabel::tr("Initial sound volume"));
- GBAvollayout->addWidget(vol, 0, 0, 1, 2);
- GBAlayout->addLayout(GBAvollayout);
- volumeBox = new QSpinBox(AGGroupBox);
- volumeBox->setRange(0, 100);
- volumeBox->setSingleStep(5);
- GBAvollayout->addWidget(volumeBox, 0, 2);
-
- CBEnableSound = new QCheckBox(AGGroupBox);
- CBEnableSound->setText(QCheckBox::tr("Enable sound"));
- GBAvollayout->addWidget(CBEnableSound, 1, 0, 1, 1);
-
- CBEnableMusic = new QCheckBox(AGGroupBox);
- CBEnableMusic->setText(QCheckBox::tr("Enable music"));
- GBAvollayout->addWidget(CBEnableMusic, 1, 1, 1, 2);
-
- GBAvollayout->setSizeConstraint(QLayout::SetMinimumSize);
-
- hr = new QFrame(AGGroupBox);
- hr->setFrameStyle(QFrame::HLine);
- hr->setLineWidth(3);
- hr->setFixedHeight(10);
- GBAlayout->addWidget(hr);
-
- CBAltDamage = new QCheckBox(AGGroupBox);
- CBAltDamage->setText(QCheckBox::tr("Alternative damage show"));
- GBAlayout->addWidget(CBAltDamage);
-
- gbTBLayout->addWidget(AGGroupBox, 0, 1, 3, 1);
+ connect(mapper, SIGNAL(mapped(int)), this, SLOT(colorButtonClicked(int)));
}
previousQuality = this->SLQuality->value();
@@ -499,3 +528,25 @@
CBTeamName->setVisible(enabled);
LblNoEditTeam->setVisible(!enabled);
}
+
+void PageOptions::colorButtonClicked(int i)
+{
+ if(i < 0 || i >= m_colorButtons.size())
+ return;
+
+ QPalette p = m_colorButtons[i]->palette();
+ QColor c = QColorDialog::getColor(p.color(QPalette::Button));
+
+ if(c.isValid())
+ {
+ DataManager::instance().colorsModel()->item(i)->setData(c);
+ m_colorButtons[i]->setStyleSheet(QString("background: %1").arg(c.name()));
+ }
+}
+
+void PageOptions::onColorModelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)
+{
+ QStandardItemModel * model = DataManager::instance().colorsModel();
+
+ m_colorButtons[topLeft.row()]->setStyleSheet(QString("background: %1").arg(model->item(topLeft.row())->data().value<QColor>().name()));
+}
--- a/QTfrontend/ui/page/pageoptions.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/page/pageoptions.h Thu Aug 30 13:02:19 2012 -0400
@@ -23,6 +23,7 @@
class FPSEdit;
class IconedGroupBox;
+class QSignalMapper;
class PageOptions : public AbstractPage
{
@@ -91,6 +92,7 @@
QPushButton *BtnNewTeam;
QPushButton *BtnEditTeam;
QPushButton *BtnDeleteTeam;
+ QList<QPushButton *> m_colorButtons;
private slots:
void forceFullscreen(int index);
@@ -101,6 +103,8 @@
void requestEditSelectedTeam();
void requestDeleteSelectedTeam();
void savePwdChanged(int state);
+ void colorButtonClicked(int i);
+ void onColorModelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
};
#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/page/pagevideos.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,1136 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QGridLayout>
+#include <QPushButton>
+#include <QGroupBox>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QLabel>
+#include <QLineEdit>
+#include <QSpinBox>
+#include <QTableWidget>
+#include <QDir>
+#include <QProgressBar>
+#include <QStringList>
+#include <QDesktopServices>
+#include <QUrl>
+#include <QList>
+#include <QMessageBox>
+#include <QHeaderView>
+#include <QKeyEvent>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QFileSystemWatcher>
+#include <QDateTime>
+#include <QRegExp>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QXmlStreamReader>
+
+#include "hwconsts.h"
+#include "pagevideos.h"
+#include "igbox.h"
+#include "libav_iteraction.h"
+#include "gameuiconfig.h"
+#include "recorder.h"
+#include "ask_quit.h"
+#include "upload_video.h"
+
+static const QSize ThumbnailSize(350, 350*3/5);
+
+// columns in table with list of video files
+enum VideosColumns
+{
+ vcName,
+ vcSize,
+ vcProgress, // either encoding or uploading
+
+ vcNumColumns,
+};
+
+// this class is used for items in first column in file-table
+class VideoItem : public QTableWidgetItem
+{
+ // note: QTableWidgetItem is not Q_OBJECT
+
+ public:
+ VideoItem(const QString& name);
+ ~VideoItem();
+
+ QString name;
+ QString prefix; // original filename without extension
+ QString desc; // description (duration, resolution, etc...)
+ QString uploadUrl; // http://youtu.be/???????
+ HWRecorder * pRecorder; // non NULL if file is being encoded
+ QNetworkReply * pUploading; // non NULL if file is being uploaded
+ bool seen; // used when updating directory
+ float lastSizeUpdate;
+ float progress;
+
+ bool ready()
+ { return !pRecorder; }
+
+ QString path()
+ { return cfgdir->absoluteFilePath("Videos/" + name); }
+};
+
+VideoItem::VideoItem(const QString& name)
+ : QTableWidgetItem(name, UserType)
+{
+ this->name = name;
+ pRecorder = NULL;
+ pUploading = NULL;
+ lastSizeUpdate = 0;
+ progress = 0;
+}
+
+VideoItem::~VideoItem()
+{}
+
+QLayout * PageVideos::bodyLayoutDefinition()
+{
+ QGridLayout * pPageLayout = new QGridLayout();
+ pPageLayout->setColumnStretch(0, 1);
+ pPageLayout->setColumnStretch(1, 2);
+ pPageLayout->setRowStretch(0, 1);
+ pPageLayout->setRowStretch(1, 1);
+
+ // options
+ {
+ IconedGroupBox* pOptionsGroup = new IconedGroupBox(this);
+ pOptionsGroup->setIcon(QIcon(":/res/Settings.png")); // FIXME
+ pOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ pOptionsGroup->setTitle(QGroupBox::tr("Video recording options"));
+ QGridLayout * pOptLayout = new QGridLayout(pOptionsGroup);
+
+ // label for format
+ QLabel *labelFormat = new QLabel(pOptionsGroup);
+ labelFormat->setText(QLabel::tr("Format"));
+ pOptLayout->addWidget(labelFormat, 0, 0);
+
+ // list of supported formats
+ comboAVFormats = new QComboBox(pOptionsGroup);
+ pOptLayout->addWidget(comboAVFormats, 0, 1, 1, 4);
+ LibavIteraction::instance().fillFormats(comboAVFormats);
+
+ // separator
+ QFrame * hr = new QFrame(pOptionsGroup);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ pOptLayout->addWidget(hr, 1, 0, 1, 5);
+
+ // label for audio codec
+ QLabel *labelACodec = new QLabel(pOptionsGroup);
+ labelACodec->setText(QLabel::tr("Audio codec"));
+ pOptLayout->addWidget(labelACodec, 2, 0);
+
+ // list of supported audio codecs
+ comboAudioCodecs = new QComboBox(pOptionsGroup);
+ pOptLayout->addWidget(comboAudioCodecs, 2, 1, 1, 3);
+
+ // checkbox 'record audio'
+ checkRecordAudio = new QCheckBox(pOptionsGroup);
+ checkRecordAudio->setText(QCheckBox::tr("Record audio"));
+ pOptLayout->addWidget(checkRecordAudio, 2, 4);
+
+ // separator
+ hr = new QFrame(pOptionsGroup);
+ hr->setFrameStyle(QFrame::HLine);
+ hr->setLineWidth(3);
+ hr->setFixedHeight(10);
+ pOptLayout->addWidget(hr, 3, 0, 1, 5);
+
+ // label for video codec
+ QLabel *labelVCodec = new QLabel(pOptionsGroup);
+ labelVCodec->setText(QLabel::tr("Video codec"));
+ pOptLayout->addWidget(labelVCodec, 4, 0);
+
+ // list of supported video codecs
+ comboVideoCodecs = new QComboBox(pOptionsGroup);
+ pOptLayout->addWidget(comboVideoCodecs, 4, 1, 1, 4);
+
+ // label for resolution
+ QLabel *labelRes = new QLabel(pOptionsGroup);
+ labelRes->setText(QLabel::tr("Resolution"));
+ pOptLayout->addWidget(labelRes, 5, 0);
+
+ // width
+ widthEdit = new QLineEdit(pOptionsGroup);
+ widthEdit->setValidator(new QIntValidator(this));
+ pOptLayout->addWidget(widthEdit, 5, 1);
+
+ // x
+ QLabel *labelX = new QLabel(pOptionsGroup);
+ labelX->setText("X");
+ pOptLayout->addWidget(labelX, 5, 2);
+
+ // height
+ heightEdit = new QLineEdit(pOptionsGroup);
+ heightEdit->setValidator(new QIntValidator(pOptionsGroup));
+ pOptLayout->addWidget(heightEdit, 5, 3);
+
+ // checkbox 'use game resolution'
+ checkUseGameRes = new QCheckBox(pOptionsGroup);
+ checkUseGameRes->setText(QCheckBox::tr("Use game resolution"));
+ pOptLayout->addWidget(checkUseGameRes, 5, 4);
+
+ // label for framerate
+ QLabel *labelFramerate = new QLabel(pOptionsGroup);
+ labelFramerate->setText(QLabel::tr("Framerate"));
+ pOptLayout->addWidget(labelFramerate, 6, 0);
+
+ // framerate
+ framerateBox = new QSpinBox(pOptionsGroup);
+ framerateBox->setRange(1, 200);
+ framerateBox->setSingleStep(1);
+ pOptLayout->addWidget(framerateBox, 6, 1);
+
+ // button 'set default options'
+ btnDefaults = new QPushButton(pOptionsGroup);
+ btnDefaults->setText(QPushButton::tr("Set default options"));
+ pOptLayout->addWidget(btnDefaults, 7, 0, 1, 5);
+
+ pPageLayout->addWidget(pOptionsGroup, 1, 0);
+ }
+
+ // list of videos
+ {
+ IconedGroupBox* pTableGroup = new IconedGroupBox(this);
+ pTableGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME
+ pTableGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ pTableGroup->setTitle(QGroupBox::tr("Videos"));
+
+ QStringList columns;
+ columns << tr("Name");
+ columns << tr("Size");
+ columns << "";
+
+ filesTable = new QTableWidget(pTableGroup);
+ filesTable->setColumnCount(vcNumColumns);
+ filesTable->setHorizontalHeaderLabels(columns);
+ filesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ filesTable->setSelectionMode(QAbstractItemView::SingleSelection);
+ filesTable->setEditTriggers(QAbstractItemView::SelectedClicked);
+ filesTable->verticalHeader()->hide();
+ filesTable->setMinimumWidth(400);
+
+ QHeaderView * header = filesTable->horizontalHeader();
+ header->setResizeMode(vcName, QHeaderView::ResizeToContents);
+ header->setResizeMode(vcSize, QHeaderView::Fixed);
+ header->resizeSection(vcSize, 100);
+ header->setStretchLastSection(true);
+
+ btnOpenDir = new QPushButton(QPushButton::tr("Open videos directory"), pTableGroup);
+
+ QVBoxLayout *box = new QVBoxLayout(pTableGroup);
+ box->addWidget(filesTable);
+ box->addWidget(btnOpenDir);
+
+ pPageLayout->addWidget(pTableGroup, 0, 1, 2, 1);
+ }
+
+ // description
+ {
+ IconedGroupBox* pDescGroup = new IconedGroupBox(this);
+ pDescGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME
+ pDescGroup->setTitle(QGroupBox::tr("Description"));
+
+ QVBoxLayout* pDescLayout = new QVBoxLayout(pDescGroup);
+ QHBoxLayout* pTopDescLayout = new QHBoxLayout(0); // picture and text
+ QHBoxLayout* pBottomDescLayout = new QHBoxLayout(0); // buttons
+
+ // label with thumbnail picture
+ labelThumbnail = new QLabel(pDescGroup);
+ labelThumbnail->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
+ labelThumbnail->setMaximumSize(ThumbnailSize);
+ labelThumbnail->setStyleSheet(
+ "QFrame {"
+ "border: solid;"
+ "border-width: 3px;"
+ "border-color: #ffcc00;"
+ "border-radius: 4px;"
+ "}" );
+ clearThumbnail();
+ pTopDescLayout->addWidget(labelThumbnail, 2);
+
+ // label with file description
+ labelDesc = new QLabel(pDescGroup);
+ labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ labelDesc->setTextInteractionFlags(Qt::TextSelectableByMouse |
+ Qt::TextSelectableByKeyboard |
+ Qt::LinksAccessibleByMouse |
+ Qt::LinksAccessibleByKeyboard);
+ labelDesc->setTextFormat(Qt::RichText);
+ labelDesc->setOpenExternalLinks(true);
+ pTopDescLayout->addWidget(labelDesc, 1);
+
+ // buttons: play and delete
+ btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
+ btnPlay->setEnabled(false);
+ pBottomDescLayout->addWidget(btnPlay);
+ btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
+ btnDelete->setEnabled(false);
+ pBottomDescLayout->addWidget(btnDelete);
+ btnToYouTube = new QPushButton(QPushButton::tr("Upload to YouTube"), pDescGroup);
+ btnToYouTube->setEnabled(false);
+ pBottomDescLayout->addWidget(btnToYouTube);
+
+ pDescLayout->addStretch(1);
+ pDescLayout->addLayout(pTopDescLayout, 0);
+ pDescLayout->addStretch(1);
+ pDescLayout->addLayout(pBottomDescLayout, 0);
+
+ pPageLayout->addWidget(pDescGroup, 0, 0);
+ }
+
+ return pPageLayout;
+}
+
+QLayout * PageVideos::footerLayoutDefinition()
+{
+ return NULL;
+}
+
+void PageVideos::connectSignals()
+{
+ connect(checkUseGameRes, SIGNAL(stateChanged(int)), this, SLOT(changeUseGameRes(int)));
+ connect(checkRecordAudio, SIGNAL(stateChanged(int)), this, SLOT(changeRecordAudio(int)));
+ connect(comboAVFormats, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAVFormat(int)));
+ connect(btnDefaults, SIGNAL(clicked()), this, SLOT(setDefaultOptions()));
+ connect(filesTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellDoubleClicked(int, int)));
+ connect(filesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int, int)));
+ connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged(int,int,int,int)));
+ connect(btnPlay, SIGNAL(clicked()), this, SLOT(playSelectedFile()));
+ connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles()));
+ connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube()));
+ connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory()));
+}
+
+PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent),
+ config(0), netManager(0)
+{
+ nameChangedFromCode = false;
+ numRecorders = 0;
+ numUploads = 0;
+ initPage();
+}
+
+void PageVideos::init(GameUIConfig * config)
+{
+ this->config = config;
+
+ QString path = cfgdir->absolutePath() + "/Videos";
+ QFileSystemWatcher * pWatcher = new QFileSystemWatcher(this);
+ pWatcher->addPath(path);
+ connect(pWatcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(updateFileList(const QString &)));
+ updateFileList(path);
+
+ startEncoding(); // this is for videos recorded from demos which were executed directly (without frontend)
+}
+
+// user changed file format, we need to update list of codecs
+void PageVideos::changeAVFormat(int index)
+{
+ // remember selected codecs
+ QString prevVCodec = videoCodec();
+ QString prevACodec = audioCodec();
+
+ // clear lists of codecs
+ comboVideoCodecs->clear();
+ comboAudioCodecs->clear();
+
+ // get list of codecs for specified format
+ LibavIteraction::instance().fillCodecs(comboAVFormats->itemData(index).toString(), comboVideoCodecs, comboAudioCodecs);
+
+ // disable audio if there is no audio codec
+ if (comboAudioCodecs->count() == 0)
+ {
+ checkRecordAudio->setChecked(false);
+ checkRecordAudio->setEnabled(false);
+ }
+ else
+ checkRecordAudio->setEnabled(true);
+
+ // restore selected codecs if possible
+ int iVCodec = comboVideoCodecs->findData(prevVCodec);
+ if (iVCodec != -1)
+ comboVideoCodecs->setCurrentIndex(iVCodec);
+ int iACodec = comboAudioCodecs->findData(prevACodec);
+ if (iACodec != -1)
+ comboAudioCodecs->setCurrentIndex(iACodec);
+}
+
+// user switched checkbox 'use game resolution'
+void PageVideos::changeUseGameRes(int state)
+{
+ if (state && config)
+ {
+ // set resolution to game resolution
+ QRect resolution = config->vid_Resolution();
+ widthEdit->setText(QString::number(resolution.width()));
+ heightEdit->setText(QString::number(resolution.height()));
+ }
+ widthEdit->setEnabled(!state);
+ heightEdit->setEnabled(!state);
+}
+
+// user switched checkbox 'record audio'
+void PageVideos::changeRecordAudio(int state)
+{
+ comboAudioCodecs->setEnabled(!!state);
+}
+
+void PageVideos::setDefaultCodecs()
+{
+ if (tryCodecs("mp4", "libx264", "libmp3lame"))
+ return;
+ if (tryCodecs("mp4", "libx264", "libfaac"))
+ return;
+ if (tryCodecs("mp4", "libx264", "libvo_aacenc"))
+ return;
+ if (tryCodecs("mp4", "libx264", "aac"))
+ return;
+ if (tryCodecs("mp4", "libx264", "mp2"))
+ return;
+ if (tryCodecs("avi", "libxvid", "libmp3lame"))
+ return;
+ if (tryCodecs("avi", "libxvid", "ac3_fixed"))
+ return;
+ if (tryCodecs("avi", "libxvid", "mp2"))
+ return;
+ if (tryCodecs("avi", "mpeg4", "libmp3lame"))
+ return;
+ if (tryCodecs("avi", "mpeg4", "ac3_fixed"))
+ return;
+ if (tryCodecs("avi", "mpeg4", "mp2"))
+ return;
+
+ // this shouldn't happen, just in case
+ if (tryCodecs("ogg", "libtheora", "libvorbis"))
+ return;
+ tryCodecs("ogg", "libtheora", "flac");
+}
+
+void PageVideos::setDefaultOptions()
+{
+ framerateBox->setValue(25);
+ checkRecordAudio->setChecked(true);
+ checkUseGameRes->setChecked(true);
+ setDefaultCodecs();
+}
+
+bool PageVideos::tryCodecs(const QString & format, const QString & vcodec, const QString & acodec)
+{
+ // first we should change format
+ int iFormat = comboAVFormats->findData(format);
+ if (iFormat == -1)
+ return false;
+ comboAVFormats->setCurrentIndex(iFormat);
+ // format was changed, so lists of codecs were automatically updated to codecs supported by this format
+
+ // try to find video codec
+ int iVCodec = comboVideoCodecs->findData(vcodec);
+ if (iVCodec == -1)
+ return false;
+ comboVideoCodecs->setCurrentIndex(iVCodec);
+
+ // try to find audio codec
+ int iACodec = comboAudioCodecs->findData(acodec);
+ if (iACodec == -1 && checkRecordAudio->isChecked())
+ return false;
+ if (iACodec != -1)
+ comboAudioCodecs->setCurrentIndex(iACodec);
+
+ return true;
+}
+
+// get file size as string
+static QString FileSizeStr(const QString & path)
+{
+ quint64 size = QFileInfo(path).size();
+
+ quint64 KiB = 1024;
+ quint64 MiB = 1024*KiB;
+ quint64 GiB = 1024*MiB;
+ QString sizeStr;
+ if (size >= GiB)
+ return QString("%1 GiB").arg(QString::number(float(size)/GiB, 'f', 2));
+ if (size >= MiB)
+ return QString("%1 MiB").arg(QString::number(float(size)/MiB, 'f', 2));
+ if (size >= KiB)
+ return QString("%1 KiB").arg(QString::number(float(size)/KiB, 'f', 2));
+ return PageVideos::tr("%1 bytes").arg(QString::number(size));
+}
+
+// set file size in file list in specified row
+void PageVideos::updateSize(int row)
+{
+ VideoItem * item = nameItem(row);
+ QString path = item->ready()? item->path() : cfgdir->absoluteFilePath("VideoTemp/" + item->pRecorder->name);
+ filesTable->item(row, vcSize)->setText(FileSizeStr(path));
+}
+
+// There is a button 'Open videos dir', so it is possible that user will open
+// this dir and rename/delete some files there, so we should handle this.
+void PageVideos::updateFileList(const QString & path)
+{
+ // mark all files as non seen
+ int numRows = filesTable->rowCount();
+ for (int i = 0; i < numRows; i++)
+ nameItem(i)->seen = false;
+
+ QStringList files = QDir(path).entryList(QDir::Files);
+ foreach (const QString & name, files)
+ {
+ int row = -1;
+ foreach (QTableWidgetItem * item, filesTable->findItems(name, Qt::MatchExactly))
+ {
+ if (item->type() != QTableWidgetItem::UserType || !((VideoItem*)item)->ready())
+ continue;
+ row = item->row();
+ break;
+ }
+ if (row == -1)
+ row = appendRow(name);
+ VideoItem * item = nameItem(row);
+ item->seen = true;
+ item->desc = "";
+ updateSize(row);
+ }
+
+ // remove all non seen files
+ for (int i = 0; i < filesTable->rowCount();)
+ {
+ VideoItem * item = nameItem(i);
+ if (item->ready() && !item->seen)
+ filesTable->removeRow(i);
+ else
+ i++;
+ }
+}
+
+void PageVideos::addRecorder(HWRecorder* pRecorder)
+{
+ int row = appendRow(pRecorder->name);
+ VideoItem * item = nameItem(row);
+ item->pRecorder = pRecorder;
+ pRecorder->item = item;
+
+ // add progress bar
+ QProgressBar * progressBar = new QProgressBar(filesTable);
+ progressBar->setMinimum(0);
+ progressBar->setMaximum(10000);
+ progressBar->setValue(0);
+ connect(pRecorder, SIGNAL(onProgress(float)), this, SLOT(updateProgress(float)));
+ connect(pRecorder, SIGNAL(encodingFinished(bool)), this, SLOT(encodingFinished(bool)));
+ filesTable->setCellWidget(row, vcProgress, progressBar);
+
+ numRecorders++;
+}
+
+void PageVideos::setProgress(int row, VideoItem* item, float value)
+{
+ QProgressBar * progressBar = (QProgressBar*)filesTable->cellWidget(row, vcProgress);
+ progressBar->setValue(value*10000);
+ progressBar->setFormat(QString("%1%").arg(value*100, 0, 'f', 2));
+ item->progress = value;
+}
+
+void PageVideos::updateProgress(float value)
+{
+ HWRecorder * pRecorder = (HWRecorder*)sender();
+ VideoItem * item = pRecorder->item;
+ int row = filesTable->row(item);
+
+ // update file size every percent
+ if (value - item->lastSizeUpdate > 0.01)
+ {
+ updateSize(row);
+ item->lastSizeUpdate = value;
+ }
+
+ setProgress(row, item, value);
+}
+
+void PageVideos::encodingFinished(bool success)
+{
+ numRecorders--;
+
+ HWRecorder * pRecorder = (HWRecorder*)sender();
+ VideoItem * item = (VideoItem*)pRecorder->item;
+ int row = filesTable->row(item);
+
+ if (success)
+ {
+ // move file to destination
+ success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + item->name);
+ if (!success)
+ {
+ // unable to rename for some reason (maybe user entered incorrect name);
+ // try to use temp name instead.
+ success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + pRecorder->name);
+ if (success)
+ setName(item, pRecorder->name);
+ }
+ }
+
+ if (!success)
+ {
+ filesTable->removeRow(row);
+ return;
+ }
+
+ filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
+ item->pRecorder = NULL;
+ updateSize(row);
+ updateDescription();
+}
+
+void PageVideos::cellDoubleClicked(int row, int column)
+{
+ play(row);
+}
+
+void PageVideos::cellChanged(int row, int column)
+{
+ // user can only edit name
+ if (column != vcName || nameChangedFromCode)
+ return;
+
+ // user has edited filename, so we should rename the file
+ VideoItem * item = nameItem(row);
+ QString oldName = item->name;
+ QString newName = item->text();
+ if (!newName.contains('.')) // user forgot an extension
+ {
+ // restore old extension
+ int pt = oldName.lastIndexOf('.');
+ if (pt != -1)
+ {
+ newName += oldName.right(oldName.length() - pt);
+ setName(item, newName);
+ }
+ }
+#ifdef Q_WS_WIN
+ // there is a bug in qt, QDir::rename() doesn't fail on such names but damages files
+ if (newName.contains(QRegExp("[\"*:<>?\/|]")))
+ {
+ setName(item, oldName);
+ return;
+ }
+#endif
+ if (item->ready() && !cfgdir->rename("Videos/" + oldName, "Videos/" + newName))
+ {
+ // unable to rename for some reason (maybe user entered incorrect name),
+ // therefore restore old name in cell
+ setName(item, oldName);
+ return;
+ }
+ item->name = newName;
+ updateDescription();
+}
+
+void PageVideos::setName(VideoItem * item, const QString & newName)
+{
+ nameChangedFromCode = true;
+ item->setText(newName);
+ nameChangedFromCode = false;
+ item->name = newName;
+}
+
+int PageVideos::appendRow(const QString & name)
+{
+ int row = filesTable->rowCount();
+ filesTable->setRowCount(row+1);
+
+ // add 'name' item
+ QTableWidgetItem * item = new VideoItem(name);
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+ nameChangedFromCode = true;
+ filesTable->setItem(row, vcName, item);
+ nameChangedFromCode = false;
+
+ // add 'size' item
+ item = new QTableWidgetItem();
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+ item->setTextAlignment(Qt::AlignRight);
+ filesTable->setItem(row, vcSize, item);
+
+ // add 'progress' item
+ item = new QTableWidgetItem();
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+ filesTable->setItem(row, vcProgress, item);
+
+ return row;
+}
+
+VideoItem* PageVideos::nameItem(int row)
+{
+ return (VideoItem*)filesTable->item(row, vcName);
+}
+
+void PageVideos::clearThumbnail()
+{
+ // add empty (transparent) image for proper sizing
+ QPixmap pic(ThumbnailSize);
+ pic.fill(QColor(0,0,0,0));
+ labelThumbnail->setPixmap(pic);
+}
+
+void PageVideos::updateDescription()
+{
+ VideoItem * item = nameItem(filesTable->currentRow());
+ if (!item)
+ {
+ // nothing is selected => clear description and return
+ labelDesc->clear();
+ clearThumbnail();
+ btnPlay->setEnabled(false);
+ btnDelete->setEnabled(false);
+ btnToYouTube->setEnabled(false);
+ return;
+ }
+
+ btnPlay->setEnabled(item->ready());
+ btnToYouTube->setEnabled(item->ready());
+ btnDelete->setEnabled(true);
+ btnDelete->setText(item->ready()? QPushButton::tr("Delete") : QPushButton::tr("Cancel"));
+ btnToYouTube->setText(item->pUploading? QPushButton::tr("Cancel uploading") : QPushButton::tr("Upload to YouTube"));
+
+ // construct string with desctiption of this file to display it
+ QString desc = item->name + "\n\n";
+
+ if (!item->ready())
+ desc += tr("(in progress...)");
+ else
+ {
+ QString path = item->path();
+ desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + '\n';
+ desc += tr("Size: ") + FileSizeStr(path) + '\n';
+ if (item->desc.isEmpty())
+ {
+ // Extract description from file;
+ // It will contain duration, resolution, etc and also comment added by hwengine.
+ item->desc = LibavIteraction::instance().getFileInfo(path);
+
+ // extract prefix (original name) from description (it is enclosed in prefix[???]prefix)
+ int prefixBegin = item->desc.indexOf("prefix[");
+ int prefixEnd = item->desc.indexOf("]prefix");
+ if (prefixBegin != -1 && prefixEnd != -1)
+ {
+ item->prefix = item->desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7));
+ item->desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin);
+ }
+ }
+ desc += item->desc + '\n';
+ }
+
+ if (item->prefix.isEmpty())
+ {
+ // try to extract prefix from file name instead
+ if (item->ready())
+ item->prefix = item->name;
+ else
+ item->prefix = item->pRecorder->name;
+
+ // remove extension
+ int pt = item->prefix.lastIndexOf('.');
+ if (pt != -1)
+ item->prefix.truncate(pt);
+ }
+
+ if (item->ready() && item->uploadUrl.isEmpty())
+ {
+ // try to load url from file
+ QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
+ if (!file->open(QIODevice::ReadOnly))
+ item->uploadUrl = "no";
+ else
+ {
+ QByteArray data = file->readAll();
+ file->close();
+ item->uploadUrl = QString::fromUtf8(data.data());
+ }
+ }
+ if (item->uploadUrl != "no")
+ desc += QString("<a href=\"%1\" style=\"color: white;\">%1</a>").arg(item->uploadUrl);
+ desc.replace("\n", "<br/>");
+
+ labelDesc->setText(desc);
+
+ if (!item->prefix.isEmpty())
+ {
+ QString thumbName = cfgdir->absoluteFilePath("VideoTemp/" + item->prefix);
+ QPixmap pic;
+ if (pic.load(thumbName + ".png") || pic.load(thumbName + ".bmp"))
+ {
+ if (pic.height()*ThumbnailSize.width() > pic.width()*ThumbnailSize.height())
+ pic = pic.scaledToWidth(ThumbnailSize.width());
+ else
+ pic = pic.scaledToHeight(ThumbnailSize.height());
+ labelThumbnail->setPixmap(pic);
+ }
+ else
+ clearThumbnail();
+ }
+}
+
+// user selected another cell, so we should change description
+void PageVideos::currentCellChanged(int row, int column, int previousRow, int previousColumn)
+{
+ updateDescription();
+}
+
+// open video file in external media player
+void PageVideos::play(int row)
+{
+ VideoItem * item = nameItem(row);
+ if (item && item->ready())
+ QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(item->path())));
+}
+
+void PageVideos::playSelectedFile()
+{
+ int index = filesTable->currentRow();
+ if (index != -1)
+ play(index);
+}
+
+void PageVideos::deleteSelectedFiles()
+{
+ int index = filesTable->currentRow();
+ if (index == -1)
+ return;
+
+ VideoItem * item = nameItem(index);
+ if (!item)
+ return;
+
+ // ask user if (s)he is serious
+ if (QMessageBox::question(this,
+ tr("Are you sure?"),
+ tr("Do you really want do remove %1?").arg(item->name),
+ QMessageBox::Yes | QMessageBox::No)
+ != QMessageBox::Yes)
+ return;
+
+ // remove
+ if (!item->ready())
+ item->pRecorder->deleteLater();
+ else
+ cfgdir->remove("Videos/" + item->name);
+
+// this code is for removing several files when multiple selection is enabled
+#if 0
+ QList<QTableWidgetItem*> items = filesTable->selectedItems();
+ int num = items.size() / vcNumColumns;
+ if (num == 0)
+ return;
+
+ // ask user if (s)he is serious
+ if (QMessageBox::question(this,
+ tr("Are you sure?"),
+ tr("Do you really want do remove %1 file(s)?").arg(num),
+ QMessageBox::Yes | QMessageBox::No)
+ != QMessageBox::Yes)
+ return;
+
+ // remove
+ foreach (QTableWidgetItem * witem, items)
+ {
+ if (witem->type() != QTableWidgetItem::UserType)
+ continue;
+ VideoItem * item = (VideoItem*)witem;
+ if (!item->ready())
+ item->pRecorder->deleteLater();
+ else
+ cfgdir->remove("Videos/" + item->name);
+ }
+#endif
+}
+
+void PageVideos::keyPressEvent(QKeyEvent * pEvent)
+{
+ if (filesTable->hasFocus())
+ {
+ if (pEvent->key() == Qt::Key_Delete)
+ {
+ deleteSelectedFiles();
+ return;
+ }
+ if (pEvent->key() == Qt::Key_Enter) // doesn't work
+ {
+ playSelectedFile();
+ return;
+ }
+ }
+ AbstractPage::keyPressEvent(pEvent);
+}
+
+void PageVideos::openVideosDirectory()
+{
+ QString path = QDir::toNativeSeparators(cfgdir->absolutePath() + "/Videos");
+ QDesktopServices::openUrl(QUrl("file:///" + path));
+}
+
+// clear VideoTemp directory (except for thumbnails and upload links)
+void PageVideos::clearTemp()
+{
+ QDir temp(cfgdir->absolutePath() + "/VideoTemp");
+ QStringList files = temp.entryList(QDir::Files);
+ foreach (const QString& file, files)
+ {
+ if (!file.endsWith(".bmp") && !file.endsWith(".png") && !file.endsWith("-url.txt"))
+ temp.remove(file);
+ }
+}
+
+bool PageVideos::tryQuit(HWForm * form)
+{
+ bool quit = true;
+ if (numRecorders != 0 || numUploads != 0)
+ {
+ // ask user what to do - abort or wait
+ HWAskQuitDialog * askd = new HWAskQuitDialog(this, form);
+ askd->deleteLater();
+ quit = askd->exec();
+ }
+ if (quit)
+ clearTemp();
+ return quit;
+}
+
+// returns multi-line string with list of videos in progress
+/* it will look like this:
+foo.avi (15.21% - encoding)
+bar.avi (18.21% - uploading)
+*/
+QString PageVideos::getVideosInProgress()
+{
+ QString list = "";
+ int count = filesTable->rowCount();
+ for (int i = 0; i < count; i++)
+ {
+ VideoItem * item = nameItem(i);
+ QString process;
+ if (!item->ready())
+ process = tr("encoding");
+ else if (item->pUploading)
+ process = tr("uploading");
+ else
+ continue;
+ float progress = 100*item->progress;
+ if (progress > 99.99)
+ progress = 99.99; // displaying 100% may be confusing
+ list += item->name + " (" + QString::number(progress, 'f', 2) + "% - " + process + ")\n";
+ }
+ return list;
+}
+
+void PageVideos::startEncoding(const QByteArray & record)
+{
+ QDir videoTempDir(cfgdir->absolutePath() + "/VideoTemp/");
+ QStringList files = videoTempDir.entryList(QStringList("*.txtout"), QDir::Files);
+ foreach (const QString & str, files)
+ {
+ QString prefix = str;
+ prefix.chop(7); // remove ".txtout"
+ videoTempDir.rename(prefix + ".txtout", prefix + ".txtin"); // rename this file to not open it twice
+
+ HWRecorder* pRecorder = new HWRecorder(config, prefix);
+
+ if (!record.isEmpty())
+ pRecorder->EncodeVideo(record);
+ else
+ {
+ // this is for videos recorded from demos which were executed directly (without frontend)
+ QFile demofile(videoTempDir.absoluteFilePath(prefix + ".hwd"));
+ if (!demofile.open(QIODevice::ReadOnly))
+ continue;
+ QByteArray demo = demofile.readAll();
+ if (demo.isEmpty())
+ continue;
+ pRecorder->EncodeVideo(demo);
+ }
+ addRecorder(pRecorder);
+ }
+}
+
+VideoItem * PageVideos::itemFromReply(QNetworkReply* reply, int & row)
+{
+ VideoItem * item = NULL;
+ int count = filesTable->rowCount();
+ // find corresponding item (maybe there is a better way to implement this?)
+ for (int i = 0; i < count; i++)
+ {
+ item = nameItem(i);
+ if (item->pUploading == reply)
+ {
+ row = i;
+ break;
+ }
+ }
+ return item;
+}
+
+void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
+{
+ QNetworkReply* reply = (QNetworkReply*)sender();
+ int row;
+ VideoItem * item = itemFromReply(reply, row);
+ setProgress(row, item, bytesSent*1.0/bytesTotal);
+}
+
+void PageVideos::uploadFinished()
+{
+ QNetworkReply* reply = (QNetworkReply*)sender();
+ reply->deleteLater();
+
+ int row;
+ VideoItem * item = itemFromReply(reply, row);
+ if (!item)
+ return;
+
+ item->pUploading = NULL;
+
+ // extract video id from reply
+ QString videoid;
+ QXmlStreamReader xml(reply);
+ while (!xml.atEnd())
+ {
+ xml.readNext();
+ if (xml.qualifiedName() == "yt:videoid")
+ {
+ videoid = xml.readElementText();
+ break;
+ }
+ }
+
+ if (!videoid.isEmpty())
+ {
+ item->uploadUrl = "http://youtu.be/" + videoid;
+ updateDescription();
+
+ // save url in file
+ QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
+ if (file->open(QIODevice::WriteOnly))
+ {
+ file->write(item->uploadUrl.toUtf8());
+ file->close();
+ }
+ }
+
+ filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
+ numUploads--;
+}
+
+// this will protect saved youtube password from those who cannot read source code
+static QString protectPass(QString str)
+{
+ QByteArray array = str.toUtf8();
+ for (int i = 0; i < array.size(); i++)
+ array[i] = array[i] ^ 0xC4 ^ i;
+ array = array.toBase64();
+ return QString::fromAscii(array.data());
+}
+
+static QString unprotectPass(QString str)
+{
+ QByteArray array = QByteArray::fromBase64(str.toAscii());
+ for (int i = 0; i < array.size(); i++)
+ array[i] = array[i] ^ 0xC4 ^ i;
+ return QString::fromUtf8(array);
+}
+
+void PageVideos::uploadToYouTube()
+{
+ int row = filesTable->currentRow();
+ VideoItem * item = nameItem(row);
+
+ if (item->pUploading)
+ {
+ if (QMessageBox::question(this,
+ tr("Are you sure?"),
+ tr("Do you really want do cancel uploading %1?").arg(item->name),
+ QMessageBox::Yes | QMessageBox::No)
+ != QMessageBox::Yes)
+ return;
+ item->pUploading->deleteLater();
+ filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
+ numUploads--;
+ return;
+ }
+
+ if (!netManager)
+ netManager = new QNetworkAccessManager(this);
+
+ HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager);
+ dlg->deleteLater();
+ if (config->value("youtube/save").toBool())
+ {
+ dlg->cbSave->setChecked(true);
+ dlg->leAccount->setText(config->value("youtube/name").toString());
+ dlg->lePassword->setText(unprotectPass(config->value("youtube/pswd").toString()));
+ }
+
+ bool result = dlg->exec();
+
+ if (dlg->cbSave->isChecked())
+ {
+ config->setValue("youtube/save", true);
+ config->setValue("youtube/name", dlg->leAccount->text());
+ config->setValue("youtube/pswd", protectPass(dlg->lePassword->text()));
+ }
+ else
+ {
+ config->setValue("youtube/save", false);
+ config->setValue("youtube/name", "");
+ config->setValue("youtube/pswd", "");
+ }
+
+ if (!result)
+ return;
+
+ QNetworkRequest request(QUrl(dlg->location));
+ request.setRawHeader("Content-Type", "application/octet-stream");
+
+ QFile * file = new QFile(item->path(), this);
+ if (!file->open(QIODevice::ReadOnly))
+ return;
+
+ // add progress bar
+ QProgressBar * progressBar = new QProgressBar(filesTable);
+ progressBar->setMinimum(0);
+ progressBar->setMaximum(10000);
+ progressBar->setValue(0);
+ // make it different from progress-bar used during encoding (use blue color)
+ progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" );
+ filesTable->setCellWidget(row, vcProgress, progressBar);
+
+ QNetworkReply* reply = netManager->put(request, file);
+ file->setParent(reply); // automatically close file when needed
+ item->pUploading = reply;
+ connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
+ connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
+ numUploads++;
+
+ updateDescription();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/page/pagevideos.h Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,125 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+#ifndef PAGE_VIDEOS_H
+#define PAGE_VIDEOS_H
+
+#include "AbstractPage.h"
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class GameUIConfig;
+class HWRecorder;
+class VideoItem;
+class HWForm;
+
+class PageVideos : public AbstractPage
+{
+ Q_OBJECT
+
+ public:
+ PageVideos(QWidget* parent = 0);
+
+ QSpinBox *framerateBox;
+ QLineEdit *widthEdit;
+ QLineEdit *heightEdit;
+ QCheckBox *checkUseGameRes;
+ QCheckBox *checkRecordAudio;
+
+ QString format()
+ { return comboAVFormats->itemData(comboAVFormats->currentIndex()).toString(); }
+
+ QString videoCodec()
+ { return comboVideoCodecs->itemData(comboVideoCodecs->currentIndex()).toString(); }
+
+ QString audioCodec()
+ { return comboAudioCodecs->itemData(comboAudioCodecs->currentIndex()).toString(); }
+
+ void setDefaultCodecs();
+ bool tryCodecs(const QString & format, const QString & vcodec, const QString & acodec);
+ void addRecorder(HWRecorder* pRecorder);
+ bool tryQuit(HWForm *form);
+ QString getVideosInProgress(); // get multi-line string with list of videos in progress
+ void startEncoding(const QByteArray & record = QByteArray());
+ void init(GameUIConfig * config);
+
+ private:
+ // virtuals from AbstractPage
+ QLayout * bodyLayoutDefinition();
+ QLayout * footerLayoutDefinition();
+ void connectSignals();
+
+ // virtual from QWidget
+ void keyPressEvent(QKeyEvent * pEvent);
+
+ void setName(VideoItem * item, const QString & newName);
+ void updateSize(int row);
+ int appendRow(const QString & name);
+ VideoItem* nameItem(int row);
+ void play(int row);
+ void updateDescription();
+ void clearTemp();
+ void clearThumbnail();
+ void setProgress(int row, VideoItem* item, float value);
+ VideoItem * itemFromReply(QNetworkReply* reply, int & row);
+
+ GameUIConfig * config;
+ QNetworkAccessManager* netManager;
+
+ // options group
+ QComboBox *comboAVFormats;
+ QComboBox *comboVideoCodecs;
+ QComboBox *comboAudioCodecs;
+ QPushButton *btnDefaults;
+
+ // file list group
+ QTableWidget *filesTable;
+ QPushButton *btnOpenDir;
+
+ // description group
+ QPushButton *btnPlay, *btnDelete, *btnToYouTube;
+ QLabel *labelDesc;
+ QLabel *labelThumbnail;
+
+ // this flag is used to distinguish if cell was changed from code or by user
+ // (in signal cellChanged)
+ bool nameChangedFromCode;
+
+ int numRecorders, numUploads;
+
+ private slots:
+ void changeAVFormat(int index);
+ void changeUseGameRes(int state);
+ void changeRecordAudio(int state);
+ void setDefaultOptions();
+ void encodingFinished(bool success);
+ void updateProgress(float value);
+ void cellDoubleClicked(int row, int column);
+ void cellChanged(int row, int column);
+ void currentCellChanged(int row, int column, int previousRow, int previousColumn);
+ void playSelectedFile();
+ void deleteSelectedFiles();
+ void openVideosDirectory();
+ void updateFileList(const QString & path);
+ void uploadToYouTube();
+ void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
+ void uploadFinished();
+};
+
+#endif // PAGE_VIDEOS_H
--- a/QTfrontend/ui/widget/chatwidget.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/widget/chatwidget.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -922,7 +922,7 @@
if(b)
{
chatNicks->insertAction(0, acKick);
-// chatNicks->insertAction(0, acBan);
+ chatNicks->insertAction(0, acBan);
}
}
--- a/QTfrontend/ui/widget/teamselect.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui/widget/teamselect.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -135,6 +135,11 @@
curPlayingTeams.erase(itPlay);
break;
}
+ else
+ {
+ qWarning() << QString("removeNetTeam: team '%1' was actually a local team!").arg(team.name());
+ break;
+ }
}
emit setEnabledGameStart(curPlayingTeams.size()>1);
}
--- a/QTfrontend/ui_hwform.cpp Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui_hwform.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -46,6 +46,7 @@
#include "pagegamestats.h"
#include "pageplayrecord.h"
#include "pagedata.h"
+#include "pagevideos.h"
#include "hwconsts.h"
void Ui_HWForm::setupUi(HWForm *HWForm)
@@ -145,4 +146,7 @@
pageFeedback = new PageFeedback();
Pages->addWidget(pageFeedback);
+
+ pageVideos = new PageVideos();
+ Pages->addWidget(pageVideos);
}
--- a/QTfrontend/ui_hwform.h Thu Aug 30 12:47:41 2012 -0400
+++ b/QTfrontend/ui_hwform.h Thu Aug 30 13:02:19 2012 -0400
@@ -43,6 +43,7 @@
class PageAdmin;
class PageNetType;
class PageDrawMap;
+class PageVideos;
class QStackedLayout;
class QFont;
class QWidget;
@@ -78,6 +79,7 @@
PageNetType *pageNetType;
PageCampaign *pageCampaign;
PageDrawMap *pageDrawMap;
+ PageVideos *pageVideos;
QStackedLayout *Pages;
QFont *font14;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/util/libav_iteraction.cpp Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,363 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "libav_iteraction.h"
+
+#if VIDEOREC
+#define __STDC_CONSTANT_MACROS
+extern "C"
+{
+#include "libavformat/avformat.h"
+}
+
+#include <QVector>
+#include <QList>
+#include <QMessageBox>
+#include <QComboBox>
+
+#include "HWApplication.h"
+
+struct Codec
+{
+ CodecID id;
+ bool isAudio;
+ QString shortName; // used for identification
+ QString longName; // used for displaying to user
+ bool isRecomended;
+};
+
+struct Format
+{
+ QString shortName;
+ QString longName;
+ bool isRecomended;
+ QString extension;
+ QVector<Codec*> codecs;
+};
+
+QList<Codec> codecs;
+QMap<QString,Format> formats;
+
+// test if given format supports given codec
+bool FormatQueryCodec(AVOutputFormat *ofmt, enum CodecID codec_id)
+{
+#if LIBAVFORMAT_VERSION_MAJOR >= 54
+ return avformat_query_codec(ofmt, codec_id, FF_COMPLIANCE_NORMAL) == 1;
+#else
+ if (ofmt->codec_tag)
+ return !!av_codec_get_tag(ofmt->codec_tag, codec_id);
+ return codec_id == ofmt->video_codec || codec_id == ofmt->audio_codec;
+#endif
+}
+
+LibavIteraction::LibavIteraction()
+{
+ // initialize libav and register all codecs and formats
+ av_register_all();
+
+ // get list of all codecs
+ AVCodec* pCodec = NULL;
+ while (pCodec = av_codec_next(pCodec))
+ {
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+ if (!av_codec_is_encoder(pCodec))
+#else
+ if (!pCodec->encode)
+#endif
+ continue;
+
+ if (pCodec->type != AVMEDIA_TYPE_VIDEO && pCodec->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+
+ // this encoders seems to be buggy
+ if (strcmp(pCodec->name, "rv10") == 0 || strcmp(pCodec->name, "rv20") == 0)
+ continue;
+
+ // doesn't support stereo sound
+ if (strcmp(pCodec->name, "real_144") == 0)
+ continue;
+
+ if (!pCodec->long_name || strlen(pCodec->long_name) == 0)
+ continue;
+
+ if (pCodec->type == AVMEDIA_TYPE_VIDEO)
+ {
+ if (pCodec->supported_framerates != NULL)
+ continue;
+
+ // check if codec supports yuv 4:2:0 format
+ if (!pCodec->pix_fmts)
+ continue;
+ bool yuv420Supported = false;
+ for (const PixelFormat* pfmt = pCodec->pix_fmts; *pfmt != -1; pfmt++)
+ if (*pfmt == PIX_FMT_YUV420P)
+ {
+ yuv420Supported = true;
+ break;
+ }
+ if (!yuv420Supported)
+ continue;
+ }
+ if (pCodec->type == AVMEDIA_TYPE_AUDIO)
+ {
+ // check if codec supports signed 16-bit format
+ if (!pCodec->sample_fmts)
+ continue;
+ bool s16Supported = false;
+ for (const AVSampleFormat* pfmt = pCodec->sample_fmts; *pfmt != -1; pfmt++)
+ if (*pfmt == AV_SAMPLE_FMT_S16)
+ {
+ s16Supported = true;
+ break;
+ }
+ if (!s16Supported)
+ continue;
+ }
+ // add codec to list of codecs
+ codecs.push_back(Codec());
+ Codec & codec = codecs.back();
+ codec.id = pCodec->id;
+ codec.isAudio = pCodec->type == AVMEDIA_TYPE_AUDIO;
+ codec.shortName = pCodec->name;
+ codec.longName = pCodec->long_name;
+
+ codec.isRecomended = false;
+ if (strcmp(pCodec->name, "libx264") == 0)
+ {
+ codec.longName = "H.264/MPEG-4 Part 10 AVC (x264)";
+ codec.isRecomended = true;
+ }
+ else if (strcmp(pCodec->name, "libxvid") == 0)
+ {
+ codec.longName = "MPEG-4 Part 2 (Xvid)";
+ codec.isRecomended = true;
+ }
+ else if (strcmp(pCodec->name, "libmp3lame") == 0)
+ {
+ codec.longName = "MP3 (MPEG audio layer 3) (LAME)";
+ codec.isRecomended = true;
+ }
+ else
+ codec.longName = pCodec->long_name;
+
+ if (strcmp(pCodec->name, "mpeg4") == 0 || strcmp(pCodec->name, "ac3_fixed") == 0)
+ codec.isRecomended = true;
+
+ // FIXME: remove next line
+ //codec.longName += QString(" (%1)").arg(codec.shortName);
+ }
+
+ // get list of all formats
+ AVOutputFormat* pFormat = NULL;
+ while (pFormat = av_oformat_next(pFormat))
+ {
+ if (!pFormat->extensions)
+ continue;
+
+ // skip some strange formats to not confuse users
+ if (strstr(pFormat->long_name, "raw"))
+ continue;
+
+ Format format;
+ bool hasVideoCodec = false;
+ for (QList<Codec>::iterator codec = codecs.begin(); codec != codecs.end(); ++codec)
+ {
+ if (!FormatQueryCodec(pFormat, codec->id))
+ continue;
+ format.codecs.push_back(&*codec);
+ if (!codec->isAudio)
+ hasVideoCodec = true;
+ }
+ if (!hasVideoCodec)
+ continue;
+
+ QString ext(pFormat->extensions);
+ ext.truncate(strcspn(pFormat->extensions, ","));
+ format.extension = ext;
+ format.shortName = pFormat->name;
+ format.longName = QString("%1 (*.%2)").arg(pFormat->long_name).arg(ext);
+
+ // FIXME: remove next line
+ //format.longName += QString(" (%1)").arg(format.shortName);
+
+ format.isRecomended = strcmp(pFormat->name, "mp4") == 0 || strcmp(pFormat->name, "avi") == 0;
+
+ formats[pFormat->name] = format;
+ }
+}
+
+void LibavIteraction::fillFormats(QComboBox * pFormats)
+{
+ // first insert recomended formats
+ foreach(const Format & format, formats)
+ if (format.isRecomended)
+ pFormats->addItem(format.longName, format.shortName);
+
+ // remember where to place separator between recomended and other formats
+ int sep = pFormats->count();
+
+ // insert remaining formats
+ foreach(const Format & format, formats)
+ if (!format.isRecomended)
+ pFormats->addItem(format.longName, format.shortName);
+
+ // insert separator if necessary
+ if (sep != 0 && sep != pFormats->count())
+ pFormats->insertSeparator(sep);
+}
+
+void LibavIteraction::fillCodecs(const QString & fmt, QComboBox * pVCodecs, QComboBox * pACodecs)
+{
+ Format & format = formats[fmt];
+
+ // first insert recomended codecs
+ foreach(Codec * codec, format.codecs)
+ {
+ if (codec->isRecomended)
+ {
+ if (codec->isAudio)
+ pACodecs->addItem(codec->longName, codec->shortName);
+ else
+ pVCodecs->addItem(codec->longName, codec->shortName);
+ }
+ }
+
+ // remember where to place separators between recomended and other codecs
+ int vsep = pVCodecs->count();
+ int asep = pACodecs->count();
+
+ // insert remaining codecs
+ foreach(Codec * codec, format.codecs)
+ {
+ if (!codec->isRecomended)
+ {
+ if (codec->isAudio)
+ pACodecs->addItem(codec->longName, codec->shortName);
+ else
+ pVCodecs->addItem(codec->longName, codec->shortName);
+ }
+ }
+
+ // insert separators if necessary
+ if (vsep != 0 && vsep != pVCodecs->count())
+ pVCodecs->insertSeparator(vsep);
+ if (asep != 0 && asep != pACodecs->count())
+ pACodecs->insertSeparator(asep);
+}
+
+QString LibavIteraction::getExtension(const QString & format)
+{
+ return formats[format].extension;
+}
+
+QString tr(QString a)
+{
+ return a;
+}
+
+// get information abaout file (duration, resolution etc) in multiline string
+QString LibavIteraction::getFileInfo(const QString & filepath)
+{
+ AVFormatContext* pContext = NULL;
+ QByteArray utf8path = filepath.toUtf8();
+ if (avformat_open_input(&pContext, utf8path.data(), NULL, NULL) < 0)
+ return "";
+#if LIBAFORMAT_VERSION_MAJOR < 54
+ if (av_find_stream_info(pContext) < 0)
+#else
+ if (avformat_find_stream_info(pContext, NULL) < 0)
+#endif
+ return "";
+
+ int s = float(pContext->duration)/AV_TIME_BASE;
+ QString desc = QString(tr("Duration: %1m %2s\n")).arg(s/60).arg(s%60);
+ for (int i = 0; i < (int)pContext->nb_streams; i++)
+ {
+ AVStream* pStream = pContext->streams[i];
+ if (!pStream)
+ continue;
+ AVCodecContext* pCodec = pContext->streams[i]->codec;
+ if (!pCodec)
+ continue;
+
+ if (pCodec->codec_type == AVMEDIA_TYPE_VIDEO)
+ {
+ desc += QString(tr("Video: %1x%2, ")).arg(pCodec->width).arg(pCodec->height);
+ if (pStream->avg_frame_rate.den)
+ {
+ float fps = float(pStream->avg_frame_rate.num)/pStream->avg_frame_rate.den;
+ desc += QString(tr("%1 fps, ")).arg(fps, 0, 'f', 2);
+ }
+ }
+ else if (pCodec->codec_type == AVMEDIA_TYPE_AUDIO)
+ desc += tr("Audio: ");
+ else
+ continue;
+ AVCodec* pDecoder = avcodec_find_decoder(pCodec->codec_id);
+ desc += pDecoder? pDecoder->name : "unknown";
+ desc += "\n";
+ }
+ AVDictionaryEntry* pComment = av_dict_get(pContext->metadata, "comment", NULL, 0);
+ if (pComment)
+ desc += QString("\n") + pComment->value;
+#if LIBAFORMAT_VERSION_MAJOR < 54
+ av_close_input_file(pContext);
+#else
+ avformat_close_input(&pContext);
+#endif
+ return desc;
+}
+
+#else
+LibavIteraction::LibavIteraction()
+{
+
+}
+
+void LibavIteraction::fillFormats(QComboBox * pFormats)
+{
+ Q_UNUSED(pFormats);
+}
+
+void LibavIteraction::fillCodecs(const QString & format, QComboBox * pVCodecs, QComboBox * pACodecs)
+{
+ Q_UNUSED(format);
+ Q_UNUSED(pVCodecs);
+ Q_UNUSED(pACodecs);
+}
+
+QString LibavIteraction::getExtension(const QString & format)
+{
+ Q_UNUSED(format);
+
+ return QString();
+}
+
+QString LibavIteraction::getFileInfo(const QString & filepath)
+{
+ Q_UNUSED(filepath);
+
+ return QString();
+}
+#endif
+
+LibavIteraction & LibavIteraction::instance()
+{
+ static LibavIteraction instance;
+ return instance;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/util/libav_iteraction.h Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,49 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef LIBAV_ITERACTION
+#define LIBAV_ITERACTION
+
+#include <QComboBox>
+
+/**
+ * @brief Class for interacting with ffmpeg/libav libraries
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Singleton_pattern">singleton pattern</a>
+ */
+class LibavIteraction
+{
+ LibavIteraction();
+
+public:
+
+ static LibavIteraction & instance();
+
+ // fill combo box with known file formats
+ void fillFormats(QComboBox * pFormats);
+
+ // fill combo boxes with known codecs for given formats
+ void fillCodecs(const QString & format, QComboBox * pVCodecs, QComboBox * pACodecs);
+
+ QString getExtension(const QString & format);
+
+ // get information about file (duration, resolution etc) in multiline string
+ QString getFileInfo(const QString & filepath);
+};
+
+#endif // LIBAV_ITERACTION
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake_modules/FindFFMPEG.cmake Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,79 @@
+# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
+# Once done this will define
+#
+# FFMPEG_FOUND - system has ffmpeg or libav
+# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
+# FFMPEG_LIBRARIES - Link these to use ffmpeg
+# FFMPEG_LIBAVCODEC
+# FFMPEG_LIBAVFORMAT
+# FFMPEG_LIBAVUTIL
+#
+# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
+# Modified for other libraries by Lasse Kärkkäinen <tronic>
+# Modified for Hedgewars by Stepik777
+#
+# Redistribution and use is allowed according to the terms of the New
+# BSD license.
+#
+
+if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+ # in cache already
+ set(FFMPEG_FOUND TRUE)
+else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+ # use pkg-config to get the directories and then use these values
+ # in the FIND_PATH() and FIND_LIBRARY() calls
+ find_package(PkgConfig)
+ if (PKG_CONFIG_FOUND)
+ pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
+ pkg_check_modules(_FFMPEG_AVFORMAT libavformat)
+ pkg_check_modules(_FFMPEG_AVUTIL libavutil)
+ endif (PKG_CONFIG_FOUND)
+
+ find_path(FFMPEG_AVCODEC_INCLUDE_DIR
+ NAMES libavcodec/avcodec.h
+ PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS} /usr/include /usr/local/include /opt/local/include /sw/include
+ PATH_SUFFIXES ffmpeg libav
+ )
+
+ find_library(FFMPEG_LIBAVCODEC
+ NAMES avcodec
+ PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
+ )
+
+ find_library(FFMPEG_LIBAVFORMAT
+ NAMES avformat
+ PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
+ )
+
+ find_library(FFMPEG_LIBAVUTIL
+ NAMES avutil
+ PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
+ )
+
+ if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT)
+ set(FFMPEG_FOUND TRUE)
+ endif()
+
+ if (FFMPEG_FOUND)
+ set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
+
+ set(FFMPEG_LIBRARIES
+ ${FFMPEG_LIBAVCODEC}
+ ${FFMPEG_LIBAVFORMAT}
+ ${FFMPEG_LIBAVUTIL}
+ )
+
+ endif (FFMPEG_FOUND)
+
+ if (FFMPEG_FOUND)
+ if (NOT FFMPEG_FIND_QUIETLY)
+ message(STATUS "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
+ endif (NOT FFMPEG_FIND_QUIETLY)
+ else (FFMPEG_FOUND)
+ if (FFMPEG_FIND_REQUIRED)
+ message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil")
+ endif (FFMPEG_FIND_REQUIRED)
+ endif (FFMPEG_FOUND)
+
+endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+
--- a/gameServer/Actions.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/Actions.hs Thu Aug 30 13:02:19 2012 -0400
@@ -146,9 +146,14 @@
io $
infoM "Clients" (show ci ++ " quits: " ++ B.unpack msg)
- processAction $ AnswerClients [chan] ["BYE", msg]
when loggedIn $ processAction $ AnswerClients clientsChans ["LOBBY:LEFT", clNick, msg]
+ mapM processAction
+ [
+ AnswerClients [chan] ["BYE", msg]
+ , ModifyClient (\c -> c{logonPassed = False}) -- this will effectively hide client from others while he isn't deleted from list
+ ]
+
s <- get
put $! s{removedClients = ci `Set.insert` removedClients s}
@@ -216,7 +221,7 @@
chans <- othersChans
if master then
- if gameProgress && playersNum > 1 then
+ if playersNum > 1 then
mapM_ processAction [ChangeMaster, NoticeMessage AdminLeft, RemoveClientTeams ci, AnswerClients chans ["LEFT", clNick, msg]]
else
processAction RemoveRoom
@@ -225,7 +230,7 @@
-- when not removing room
ready <- client's isReady
- when (not master || (gameProgress && playersNum > 1)) . io $ do
+ when (not master || playersNum > 1) . io $ do
modifyRoom rnc (\r -> r{
playersIn = playersIn r - 1,
readyPlayers = if ready then readyPlayers r - 1 else readyPlayers r
@@ -418,11 +423,22 @@
processAction JoinLobby = do
chan <- client's sendChan
clientNick <- client's nick
- (lobbyNicks, clientsChans) <- liftM (unzip . Prelude.map (nick &&& sendChan) . Prelude.filter logonPassed) $! allClientsS
- mapM_ processAction $
- AnswerClients clientsChans ["LOBBY:JOINED", clientNick]
- : AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)
- : [ModifyClient (\cl -> cl{logonPassed = True}), SendServerMessage]
+ isAuthenticated <- liftM (not . B.null) $ client's webPassword
+ isAdmin <- client's isAdministrator
+ loggedInClients <- liftM (Prelude.filter logonPassed) $! allClientsS
+ let (lobbyNicks, clientsChans) = unzip . L.map (nick &&& sendChan) $ loggedInClients
+ let authenticatedNicks = L.map nick . L.filter (not . B.null . webPassword) $ loggedInClients
+ let adminsNicks = L.map nick . L.filter isAdministrator $ loggedInClients
+ let clFlags = B.concat . L.concat $ [["u" | isAuthenticated], ["a" | isAdmin]]
+ mapM_ processAction . concat $ [
+ [AnswerClients clientsChans ["LOBBY:JOINED", clientNick]]
+ , [AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)]
+ , [AnswerClients [chan] ("CLIENT_FLAGS" : "+u" : authenticatedNicks) | not $ null authenticatedNicks]
+ , [AnswerClients [chan] ("CLIENT_FLAGS" : "+a" : adminsNicks) | not $ null adminsNicks]
+ , [AnswerClients (chan : clientsChans) ["CLIENT_FLAGS", B.concat["+" , clFlags], clientNick] | not $ B.null clFlags]
+ , [ModifyClient (\cl -> cl{logonPassed = True})]
+ , [SendServerMessage]
+ ]
processAction (KickClient kickId) = do
@@ -453,9 +469,9 @@
processAction BanList = do
ch <- client's sendChan
- bans <- gets (bans . serverInfo)
+ bans <- gets (B.pack . unlines . map show . bans . serverInfo)
processAction $
- AnswerClients [ch] ["BANLIST", B.pack $ show bans]
+ AnswerClients [ch] ["BANLIST", bans]
@@ -522,9 +538,11 @@
where
kickTimeouted rnc ci = do
pq <- io $ client'sM rnc pingsQueue ci
- when (pq > 0) $
+ when (pq > 0) $ do
withStateT (\as -> as{clientIndex = Just ci}) $
processAction (ByeClient "Ping timeout")
+-- when (pq > 1) $
+-- processAction $ DeleteClient ci -- smth went wrong with client io threads, issue DeleteClient here
processAction StatsAction = do
--- a/gameServer/CoreTypes.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/CoreTypes.hs Thu Aug 30 13:02:19 2012 -0400
@@ -94,6 +94,7 @@
readyPlayers :: !Int,
isRestrictedJoins :: Bool,
isRestrictedTeams :: Bool,
+ roomBansList :: [B.ByteString],
mapParams :: Map.Map B.ByteString B.ByteString,
params :: Map.Map B.ByteString [B.ByteString]
}
@@ -111,6 +112,7 @@
0
False
False
+ []
(
Map.fromList $ Prelude.zipWith (,)
["MAP", "MAPGEN", "MAZE_SIZE", "SEED", "TEMPLATE"]
--- a/gameServer/HWProtoInRoomState.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/HWProtoInRoomState.hs Thu Aug 30 13:02:19 2012 -0400
@@ -278,6 +278,14 @@
where
engineMsg cl = toEngineMsg $ B.concat ["b", nick cl, "(team): ", msg, "\x20\x20"]
+handleCmd_inRoom ["BAN", banNick] = do
+ (_, rnc) <- ask
+ maybeClientId <- clientByNick banNick
+ let banId = fromJust maybeClientId
+ master <- liftM isMaster thisClient
+ return [ModifyRoom (\r -> r{roomBansList = (host $ rnc `client` banId) : roomBansList r}) | master && isJust maybeClientId]
+
+
handleCmd_inRoom ["LIST"] = return [] -- for old clients (<= 0.9.17)
handleCmd_inRoom (s:_) = return [ProtocolError $ "Incorrect command '" `B.append` s `B.append` "' (state: in room)"]
--- a/gameServer/HWProtoLobbyState.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/HWProtoLobbyState.hs Thu Aug 30 13:02:19 2012 -0400
@@ -70,11 +70,14 @@
let jRoomClients = map (client irnc) $ roomClients irnc jRI
let nicks = map nick jRoomClients
let chans = map sendChan (cl : jRoomClients)
+ let isBanned = host cl `elem` roomBansList jRoom
return $
if isNothing maybeRI || not sameProto then
[Warning "No such room"]
else if isRestrictedJoins jRoom then
[Warning "Joining restricted"]
+ else if isBanned then
+ [Warning "You are banned in this room"]
else if roomPassword /= password jRoom then
[NoticeMessage WrongPassword]
else
@@ -183,7 +186,7 @@
handleCmd_lobby ["RESTART_SERVER"] = do
cl <- thisClient
- return [RestartServer]
+ return [RestartServer | isAdministrator cl]
handleCmd_lobby _ = return [ProtocolError "Incorrect command (state: in lobby)"]
--- a/gameServer/ServerCore.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/gameServer/ServerCore.hs Thu Aug 30 13:02:19 2012 -0400
@@ -43,7 +43,7 @@
ClientMessage (ci, cmd) -> do
liftIO $ debugM "Clients" $ show ci ++ ": " ++ show cmd
-
+
removed <- gets removedClients
unless (ci `Set.member` removed) $ do
modify (\s -> s{clientIndex = Just ci})
@@ -75,8 +75,6 @@
(fromJust $ serverSocket si)
(coreChan si)
- return ()
-
_ <- forkIO $ timerLoop 0 $ coreChan si
startDBConnection si
--- a/hedgewars/ArgParsers.inc Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/ArgParsers.inc Thu Aug 30 13:02:19 2012 -0400
@@ -63,6 +63,21 @@
cLocaleFName:= ParamStr(17);
end;
+{$IFDEF USE_VIDEO_RECORDING}
+procedure internalStartVideoRecordingWithParameters();
+begin
+ internalStartGameWithParameters();
+ GameType:= gmtRecord;
+ cVideoFramerateNum:= StrToInt(ParamStr(18));
+ cVideoFramerateDen:= StrToInt(ParamStr(19));
+ RecPrefix:= ParamStr(20);
+ cAVFormat:= ParamStr(21);
+ cVideoCodec:= ParamStr(22);
+ cVideoQuality:= StrToInt(ParamStr(23));
+ cAudioCodec:= ParamStr(24);
+end;
+{$ENDIF}
+
procedure setVideo(screenWidth: LongInt; screenHeight: LongInt; bitsStr: LongInt);
begin
cScreenWidth:= screenWidth;
--- a/hedgewars/CMakeLists.txt Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/CMakeLists.txt Thu Aug 30 13:02:19 2012 -0400
@@ -3,6 +3,7 @@
find_package(SDL_net)
find_package(SDL_ttf)
find_package(SDL_mixer)
+find_package(FFMPEG)
include(${CMAKE_MODULE_PATH}/FindSDL_Extras.cmake)
@@ -33,7 +34,9 @@
uGame.pas
uGears.pas
uGearsHandlers.pas
+ uGearsHandlersRope.pas
uGearsRender.pas
+ uGearsUtils.pas
uIO.pas
uInputHandler.pas
uLand.pas
@@ -60,6 +63,7 @@
uTypes.pas
uUtils.pas
uVariables.pas
+ uVideoRec.pas
uVisualGears.pas
uWorld.pas
GSHandlers.inc
@@ -183,9 +187,31 @@
message(STATUS "PNG screenshots disabled per user request, using BMP format")
endif()
+if(NOT NO_VIDEOREC)
+ if(${FFMPEG_FOUND})
+ message(STATUS "Compiling with video recording")
+ include_directories(${FFMPEG_INCLUDE_DIR})
+ set(pascal_flags "-dUSE_VIDEO_RECORDING" ${pascal_flags})
+ set(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH})
+ IF (WIN32)
+ # there are some problems with linking our avwrapper as static lib, so link it as shared
+ add_library(avwrapper SHARED avwrapper.c)
+ target_link_libraries(avwrapper ${FFMPEG_LIBRARIES})
+ install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}avwrapper${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION ${target_dir})
+ ELSE()
+ add_library(avwrapper STATIC avwrapper.c)
+ set(pascal_flags "-k${FFMPEG_LIBAVCODEC}" "-k${FFMPEG_LIBAVFORMAT}" "-k${FFMPEG_LIBAVUTIL}" ${pascal_flags})
+ # set(pascal_flags "-k${LIBRARY_OUTPUT_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}avwrapper${CMAKE_STATIC_LIBRARY_SUFFIX}" ${pascal_flags})
+ ENDIF()
+ else()
+ message(STATUS "FFMPEG library not found, video recording will be disabled")
+ endif()
+else()
+ message(STATUS "Video recording disabled by user")
+endif()
+
set(fpc_flags ${noexecstack_flags} ${pascal_flags} ${hwengine_project})
-
IF(NOT APPLE)
#here is the command for standard executables or for shared library
add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/${engine_output_name}${CMAKE_EXECUTABLE_SUFFIX}"
@@ -225,10 +251,9 @@
#this command is a workaround to some inlining issues present in older
# FreePascal versions and fixed in 2.6, That version is mandatory on OSX,
# hence the command is not needed there
-if(NOT APPLE)
- add_custom_target(ENGINECLEAN COMMAND ${CMAKE_BUILD_TOOL} "clean" "${PROJECT_BINARY_DIR}" "${hedgewars_SOURCE_DIR}/hedgewars")
- add_dependencies(${engine_output_name} ENGINECLEAN)
-endif()
+# if(NOT APPLE)
+# add_custom_target(ENGINECLEAN COMMAND ${CMAKE_BUILD_TOOL} "clean" "${PROJECT_BINARY_DIR}" "${hedgewars_SOURCE_DIR}/hedgewars")
+# add_dependencies(${engine_output_name} ENGINECLEAN)
+# endif()
install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/${engine_output_name}${CMAKE_EXECUTABLE_SUFFIX}" DESTINATION ${target_dir})
-
--- a/hedgewars/GSHandlers.inc Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/GSHandlers.inc Thu Aug 30 13:02:19 2012 -0400
@@ -140,28 +140,6 @@
ScriptCall('onHogRestore', HH^.Gear^.Uid)
end;
-////////////////////////////////////////////////////////////////////////////////
-procedure CheckCollision(Gear: PGear); inline;
-begin
- if TestCollisionXwithGear(Gear, hwSign(Gear^.dX))
- or (TestCollisionYwithGear(Gear, hwSign(Gear^.dY)) <> 0) then
- Gear^.State := Gear^.State or gstCollision
- else
- Gear^.State := Gear^.State and (not gstCollision)
-end;
-
-procedure CheckCollisionWithLand(Gear: PGear); inline;
-begin
- if TestCollisionX(Gear, hwSign(Gear^.dX))
- or TestCollisionY(Gear, hwSign(Gear^.dY)) then
- Gear^.State := Gear^.State or gstCollision
- else
- Gear^.State := Gear^.State and (not gstCollision)
-end;
-
-
-////////////////////////////////////////////////////////////////////////////////
-
////////////////////////////////////////////////////////////////////////////////
procedure doStepDrowningGear(Gear: PGear);
@@ -191,11 +169,24 @@
collV, collH: LongInt;
land: word;
begin
- // clip velocity at 1.9 - over 1 per pixel, but really shouldn't cause many actual problems.
- if Gear^.dX.QWordValue > 8160437862 then
- Gear^.dX.QWordValue:= 8160437862;
- if Gear^.dY.QWordValue > 8160437862 then
- Gear^.dY.QWordValue:= 8160437862;
+ // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems.
+{$IFNDEF WEB}
+ if Gear^.dX.Round > 2 then
+ Gear^.dX.QWordValue:= 8589934592;
+ if Gear^.dY.Round > 2 then
+ Gear^.dY.QWordValue:= 8589934592;
+{$ELSE}
+ if Gear^.dX.Round > 2 then
+ begin
+ Gear^.dX.Round:= 2;
+ Gear^.dX.Frac:= 0
+ end;
+ if Gear^.dY.QWordValue > 2 then
+ begin
+ Gear^.dY.Round:= 2;
+ Gear^.dY.Frac:= 0
+ end;
+{$ENDIF}
Gear^.State := Gear^.State and (not gstCollision);
collV := 0;
collH := 0;
@@ -541,7 +532,7 @@
kick:= hwRound((hwAbs(Gear^.dX)+hwAbs(Gear^.dY)) * _20);
Gear^.dY.isNegative:= not Gear^.dY.isNegative;
Gear^.dX.isNegative:= not Gear^.dX.isNegative;
- AmmoShove(Gear, 1, kick);
+ AmmoShove(Gear, 0, kick);
for i:= 15 + kick div 10 downto 0 do
begin
particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust);
@@ -610,11 +601,10 @@
// move back to cloud layer
if yy > cWaterLine then
move:= true
- else if ((yy and LAND_HEIGHT_MASK) <> 0)
- or (xx > LAND_WIDTH + 512) or (xx < -512) then
+ else if (xx > snowRight) or (xx < snowLeft) then
move:=true
// Solid pixel encountered
- else if ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
+ else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
begin
lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible);
// If there's room below keep falling
@@ -724,8 +714,8 @@
exit
end;
Gear^.Pos:= 0;
- Gear^.X:= int2hwFloat(GetRandom(LAND_WIDTH+1024)-512);
- Gear^.Y:= int2hwFloat(750+(GetRandom(50)-25));
+ Gear^.X:= int2hwFloat(GetRandom(snowRight-snowLeft)+snowLeft);
+ Gear^.Y:= int2hwFloat(LAND_HEIGHT-1300+(GetRandom(50)-25));
Gear^.State:= Gear^.State or gstInvisible;
end
end;
@@ -924,7 +914,13 @@
Gear^.State := Gear^.State or gstAnimation
end;
exit
- end
+ end else
+ if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then
+ begin
+ DeleteGear(Gear);
+ AfterAttack;
+ exit
+ end
else
inc(Gear^.Timer);
@@ -1401,436 +1397,6 @@
end;
////////////////////////////////////////////////////////////////////////////////
-
-procedure doStepRope(Gear: PGear);
-forward;
-
-procedure doStepRopeAfterAttack(Gear: PGear);
-var
- HHGear: PGear;
-begin
- HHGear := Gear^.Hedgehog^.Gear;
- if ((HHGear^.State and gstHHDriven) = 0)
- or (CheckGearDrowning(HHGear))
- or (TestCollisionYwithGear(HHGear, 1) <> 0) then
- begin
- DeleteGear(Gear);
- isCursorVisible := false;
- ApplyAmmoChanges(HHGear^.Hedgehog^);
- exit
- end;
-
- HedgehogChAngle(HHGear);
-
- if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
- SetLittle(HHGear^.dX);
-
- if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
- HHGear^.dY := _0;
- HHGear^.X := HHGear^.X + HHGear^.dX;
- HHGear^.Y := HHGear^.Y + HHGear^.dY;
- HHGear^.dY := HHGear^.dY + cGravity;
-
- if (GameFlags and gfMoreWind) <> 0 then
- HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;
-
- if (Gear^.Message and gmAttack) <> 0 then
- begin
- Gear^.X := HHGear^.X;
- Gear^.Y := HHGear^.Y;
-
- ApplyAngleBounds(Gear^.Hedgehog^, amRope);
-
- Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX);
- Gear^.dY := -AngleCos(HHGear^.Angle);
- Gear^.Friction := _4_5 * cRopePercent;
- Gear^.Elasticity := _0;
- Gear^.State := Gear^.State and (not gsttmpflag);
- Gear^.doStep := @doStepRope;
- end
-end;
-
-procedure RopeDeleteMe(Gear, HHGear: PGear);
-begin
- with HHGear^ do
- begin
- Message := Message and (not gmAttack);
- State := (State or gstMoving) and (not gstWinner);
- end;
- DeleteGear(Gear)
-end;
-
-procedure RopeWaitCollision(Gear, HHGear: PGear);
-begin
- with HHGear^ do
- begin
- Message := Message and (not gmAttack);
- State := State or gstMoving;
- end;
- RopePoints.Count := 0;
- Gear^.Elasticity := _0;
- Gear^.doStep := @doStepRopeAfterAttack
-end;
-
-procedure doStepRopeWork(Gear: PGear);
-var
- HHGear: PGear;
- len, tx, ty, nx, ny, ropeDx, ropeDy, mdX, mdY: hwFloat;
- lx, ly, cd: LongInt;
- haveCollision,
- haveDivided: boolean;
-
-begin
- HHGear := Gear^.Hedgehog^.Gear;
-
- if ((HHGear^.State and gstHHDriven) = 0)
- or (CheckGearDrowning(HHGear)) or (Gear^.PortalCounter <> 0) then
- begin
- PlaySound(sndRopeRelease);
- RopeDeleteMe(Gear, HHGear);
- exit
- end;
-
- if (Gear^.Message and gmLeft <> 0) and (not TestCollisionXwithGear(HHGear, -1)) then
- HHGear^.dX := HHGear^.dX - _0_0002;
-
- if (Gear^.Message and gmRight <> 0) and (not TestCollisionXwithGear(HHGear, 1)) then
- HHGear^.dX := HHGear^.dX + _0_0002;
-
- // vector between hedgehog and rope attaching point
- ropeDx := HHGear^.X - Gear^.X;
- ropeDy := HHGear^.Y - Gear^.Y;
-
- if TestCollisionYwithGear(HHGear, 1) = 0 then
- begin
-
- // depending on the rope vector we know which X-side to check for collision
- // in order to find out if the hog can still be moved by gravity
- if ropeDx.isNegative = RopeDy.IsNegative then
- cd:= -1
- else
- cd:= 1;
-
- // apply gravity if there is no obstacle
- if not TestCollisionXwithGear(HHGear, cd) then
- HHGear^.dY := HHGear^.dY + cGravity;
-
- if (GameFlags and gfMoreWind) <> 0 then
- // apply wind if there's no obstacle
- if not TestCollisionXwithGear(HHGear, hwSign(cWindSpeed)) then
- HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;
- end;
-
- mdX := ropeDx + HHGear^.dX;
- mdY := ropeDy + HHGear^.dY;
- len := _1 / Distance(mdX, mdY);
- // rope vector plus hedgehog direction vector normalized
- mdX := mdX * len;
- mdY := mdY * len;
-
- // for visual purposes only
- Gear^.dX := mdX;
- Gear^.dY := mdY;
-
- /////
- tx := HHGear^.X;
- ty := HHGear^.Y;
-
- if ((Gear^.Message and gmDown) <> 0) and (Gear^.Elasticity < Gear^.Friction) then
- if not (TestCollisionXwithGear(HHGear, hwSign(ropeDx))
- or (TestCollisionYwithGear(HHGear, hwSign(ropeDy)) <> 0)) then
- Gear^.Elasticity := Gear^.Elasticity + _0_3;
-
- if ((Gear^.Message and gmUp) <> 0) and (Gear^.Elasticity > _30) then
- if not (TestCollisionXwithGear(HHGear, -hwSign(ropeDx))
- or (TestCollisionYwithGear(HHGear, -hwSign(ropeDy)) <> 0)) then
- Gear^.Elasticity := Gear^.Elasticity - _0_3;
-
- HHGear^.X := Gear^.X + mdX * Gear^.Elasticity;
- HHGear^.Y := Gear^.Y + mdY * Gear^.Elasticity;
-
- HHGear^.dX := HHGear^.X - tx;
- HHGear^.dY := HHGear^.Y - ty;
- ////
-
-
- haveDivided := false;
- // check whether rope needs dividing
-
- len := Gear^.Elasticity - _5;
- nx := Gear^.X + mdX * len;
- ny := Gear^.Y + mdY * len;
- tx := mdX * _0_3; // should be the same as increase step
- ty := mdY * _0_3;
-
- while len > _3 do
- begin
- lx := hwRound(nx);
- ly := hwRound(ny);
- if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and ((Land[ly, lx] and $FF00) <> 0) then
- begin
- ny := _1 / Distance(ropeDx, ropeDy);
- // old rope pos
- nx := ropeDx * ny;
- ny := ropeDy * ny;
-
- with RopePoints.ar[RopePoints.Count] do
- begin
- X := Gear^.X;
- Y := Gear^.Y;
- if RopePoints.Count = 0 then
- RopePoints.HookAngle := DxDy2Angle(Gear^.dY, Gear^.dX);
- b := (nx * HHGear^.dY) > (ny * HHGear^.dX);
- dLen := len
- end;
-
- with RopePoints.rounded[RopePoints.Count] do
- begin
- X := hwRound(Gear^.X);
- Y := hwRound(Gear^.Y);
- end;
-
- Gear^.X := Gear^.X + nx * len;
- Gear^.Y := Gear^.Y + ny * len;
- inc(RopePoints.Count);
- TryDo(RopePoints.Count <= MAXROPEPOINTS, 'Rope points overflow', true);
- Gear^.Elasticity := Gear^.Elasticity - len;
- Gear^.Friction := Gear^.Friction - len;
- haveDivided := true;
- break
- end;
- nx := nx - tx;
- ny := ny - ty;
-
- // len := len - _0_3 // should be the same as increase step
- len.QWordValue := len.QWordValue - _0_3.QWordValue;
- end;
-
- if not haveDivided then
- if RopePoints.Count > 0 then // check whether the last dividing point could be removed
- begin
- tx := RopePoints.ar[Pred(RopePoints.Count)].X;
- ty := RopePoints.ar[Pred(RopePoints.Count)].Y;
- mdX := tx - Gear^.X;
- mdY := ty - Gear^.Y;
- if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * (ty - HHGear^.Y) > (tx - HHGear^.X) * mdY) then
- begin
- dec(RopePoints.Count);
- Gear^.X := RopePoints.ar[RopePoints.Count].X;
- Gear^.Y := RopePoints.ar[RopePoints.Count].Y;
- Gear^.Elasticity := Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen;
- Gear^.Friction := Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen;
-
- // restore hog position
- len := _1 / Distance(mdX, mdY);
- mdX := mdX * len;
- mdY := mdY * len;
-
- HHGear^.X := Gear^.X - mdX * Gear^.Elasticity;
- HHGear^.Y := Gear^.Y - mdY * Gear^.Elasticity;
- end
- end;
-
- haveCollision := false;
- if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
- begin
- HHGear^.dX := -_0_6 * HHGear^.dX;
- haveCollision := true
- end;
- if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0 then
- begin
- HHGear^.dY := -_0_6 * HHGear^.dY;
- haveCollision := true
- end;
-
- if haveCollision and (Gear^.Message and (gmLeft or gmRight) <> 0) and (Gear^.Message and (gmUp or gmDown) <> 0) then
- begin
- HHGear^.dX := SignAs(hwAbs(HHGear^.dX) + _0_2, HHGear^.dX);
- HHGear^.dY := SignAs(hwAbs(HHGear^.dY) + _0_2, HHGear^.dY)
- end;
-
- len := hwSqr(HHGear^.dX) + hwSqr(HHGear^.dY);
- if len > _0_64 then
- begin
- len := _0_8 / hwSqrt(len);
- HHGear^.dX := HHGear^.dX * len;
- HHGear^.dY := HHGear^.dY * len;
- end;
-
- haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)]) <> 0);
-
- if not haveCollision then
- begin
- // backup gear location
- tx:= Gear^.X;
- ty:= Gear^.Y;
-
- if RopePoints.Count > 0 then
- begin
- // set gear location to the remote end of the rope, the attachment point
- Gear^.X:= RopePoints.ar[0].X;
- Gear^.Y:= RopePoints.ar[0].Y;
- end;
-
- CheckCollision(Gear);
- // if we haven't found any collision yet then check the other side too
- if (Gear^.State and gstCollision) = 0 then
- begin
- Gear^.dX.isNegative:= not Gear^.dX.isNegative;
- Gear^.dY.isNegative:= not Gear^.dY.isNegative;
- CheckCollision(Gear);
- Gear^.dX.isNegative:= not Gear^.dX.isNegative;
- Gear^.dY.isNegative:= not Gear^.dY.isNegative;
- end;
-
- haveCollision:= (Gear^.State and gstCollision) <> 0;
-
- // restore gear location
- Gear^.X:= tx;
- Gear^.Y:= ty;
- end;
-
- // if the attack key is pressed, lose rope contact as well
- if (Gear^.Message and gmAttack) <> 0 then
- haveCollision:= false;
-
- if not haveCollision then
- begin
- if (Gear^.State and gsttmpFlag) <> 0 then
- begin
- PlaySound(sndRopeRelease);
- if Gear^.Hedgehog^.CurAmmoType <> amParachute then
- RopeWaitCollision(Gear, HHGear)
- else
- RopeDeleteMe(Gear, HHGear)
- end
- end
- else
- if (Gear^.State and gsttmpFlag) = 0 then
- Gear^.State := Gear^.State or gsttmpFlag;
-end;
-
-procedure RopeRemoveFromAmmo(Gear, HHGear: PGear);
-begin
- if (Gear^.State and gstAttacked) = 0 then
- begin
- OnUsedAmmo(HHGear^.Hedgehog^);
- Gear^.State := Gear^.State or gstAttacked
- end;
- ApplyAmmoChanges(HHGear^.Hedgehog^)
-end;
-
-procedure doStepRopeAttach(Gear: PGear);
-var
- HHGear: PGear;
- tx, ty, tt: hwFloat;
-begin
- Gear^.X := Gear^.X - Gear^.dX;
- Gear^.Y := Gear^.Y - Gear^.dY;
- Gear^.Elasticity := Gear^.Elasticity + _1;
-
- HHGear := Gear^.Hedgehog^.Gear;
- DeleteCI(HHGear);
-
- if (HHGear^.State and gstMoving) <> 0 then
- begin
- if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
- SetLittle(HHGear^.dX);
- if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
- HHGear^.dY := _0;
-
- HHGear^.X := HHGear^.X + HHGear^.dX;
- Gear^.X := Gear^.X + HHGear^.dX;
-
- if TestCollisionYwithGear(HHGear, 1) <> 0 then
- begin
- CheckHHDamage(HHGear);
- HHGear^.dY := _0
- //HHGear^.State:= HHGear^.State and (not (gstHHJumping or gstHHHJump));
- end
- else
- begin
- HHGear^.Y := HHGear^.Y + HHGear^.dY;
- Gear^.Y := Gear^.Y + HHGear^.dY;
- HHGear^.dY := HHGear^.dY + cGravity;
- if (GameFlags and gfMoreWind) <> 0 then
- HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density
- end;
-
- tt := Gear^.Elasticity;
- tx := _0;
- ty := _0;
- while tt > _20 do
- begin
- if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] and $FF00) <> 0) then
- begin
- Gear^.X := Gear^.X + tx;
- Gear^.Y := Gear^.Y + ty;
- Gear^.Elasticity := tt;
- Gear^.doStep := @doStepRopeWork;
- PlaySound(sndRopeAttach);
- with HHGear^ do
- begin
- State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
- Message := Message and (not gmAttack)
- end;
-
- RopeRemoveFromAmmo(Gear, HHGear);
-
- tt := _0;
- exit
- end;
- tx := tx + Gear^.dX + Gear^.dX;
- ty := ty + Gear^.dY + Gear^.dY;
- tt := tt - _2;
- end;
- end;
-
- CheckCollision(Gear);
-
- if (Gear^.State and gstCollision) <> 0 then
- if Gear^.Elasticity < _10 then
- Gear^.Elasticity := _10000
- else
- begin
- Gear^.doStep := @doStepRopeWork;
- PlaySound(sndRopeAttach);
- with HHGear^ do
- begin
- State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
- Message := Message and (not gmAttack)
- end;
-
- RopeRemoveFromAmmo(Gear, HHGear);
-
- exit
- end;
-
- if (Gear^.Elasticity > Gear^.Friction)
- or ((Gear^.Message and gmAttack) = 0)
- or ((HHGear^.State and gstHHDriven) = 0)
- or (HHGear^.Damage > 0) then
- begin
- with Gear^.Hedgehog^.Gear^ do
- begin
- State := State and (not gstAttacking);
- Message := Message and (not gmAttack)
- end;
- DeleteGear(Gear);
- exit;
- end;
- if CheckGearDrowning(HHGear) then DeleteGear(Gear)
-end;
-
-procedure doStepRope(Gear: PGear);
-begin
- Gear^.dX := - Gear^.dX;
- Gear^.dY := - Gear^.dY;
- Gear^.doStep := @doStepRopeAttach;
- PlaySound(sndRopeShot)
-end;
-
-////////////////////////////////////////////////////////////////////////////////
procedure doStepMine(Gear: PGear);
var vg: PVisualGear;
begin
@@ -1930,7 +1496,7 @@
or TestCollisionXwithGear(Gear, -2)
or (TestCollisionYwithGear(Gear, 2) <> 0) then
begin
- if (hwAbs(Gear^.dX) > _0) or (hwAbs(Gear^.dY) > _0) then
+ if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then
begin
PlaySound(sndRopeAttach);
Gear^.dX:= _0;
@@ -2406,7 +1972,7 @@
//DrawExplosion(gX, gY, 4);
if ((GameTicks and $7) = 0) and (Random(2) = 0) then
- for i:= 1 to Random(2)+1 do
+ for i:= Random(2) downto 0 do
AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
if Gear^.Health > 0 then
@@ -2420,7 +1986,7 @@
begin
DrawExplosion(gX, gY, 4);
- for i:= 0 to Random(3) do
+ for i:= Random(3) downto 0 do
AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
end;
@@ -2438,20 +2004,12 @@
if not sticky then
begin
if ((GameTicks and $3) = 0) and (Random(1) = 0) then
- begin
- for i:= 1 to Random(2)+1 do
- begin
+ for i:= Random(2) downto 0 do
AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
- end;
- end;
end
else
- begin
- for i:= 0 to Random(3) do
- begin
+ for i:= Random(3) downto 0 do
AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
- end;
- end;
DeleteGear(Gear)
end;
@@ -3007,7 +2565,6 @@
////////////////////////////////////////////////////////////////////////////////
const cakeh = 27;
- cakeDmg = 75;
var
CakePoints: array[0..Pred(cakeh)] of record
x, y: hwFloat;
@@ -3091,6 +2648,19 @@
if Gear^.Tag < 7 then
exit;
+ dec(Gear^.Health);
+ Gear^.Timer := Gear^.Health*10;
+ if Gear^.Health mod 100 = 0 then
+ Gear^.PortalCounter:= 0;
+ // This is not seconds, but at least it is *some* feedback
+ if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then
+ begin
+ FollowGear := Gear;
+ Gear^.RenderTimer := false;
+ Gear^.doStep := @doStepCakeDown;
+ exit
+ end;
+
cakeStep(Gear);
if Gear^.Tag = 0 then
@@ -3102,18 +2672,6 @@
CakePoints[CakeI].y := Gear^.Y;
Gear^.DirAngle := DxDy2Angle(tdx, tdy);
end;
-
- dec(Gear^.Health);
- Gear^.Timer := Gear^.Health*10;
- if Gear^.Health mod 100 = 0 then
- Gear^.PortalCounter:= 0;
- // This is not seconds, but at least it is *some* feedback
- if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then
- begin
- FollowGear := Gear;
- Gear^.RenderTimer := false;
- Gear^.doStep := @doStepCakeDown
- end
end;
procedure doStepCakeUp(Gear: PGear);
@@ -3693,10 +3251,6 @@
Gear^.X := HHGear^.X;
Gear^.Y := HHGear^.Y;
- // For some reason I need to reapply followgear here, something else grabs it otherwise.
- // This is probably not needed anymore
- if not CurrentTeam^.ExtDriven then
- FollowGear := HHGear;
if not isUnderWater and hasBorder and ((HHGear^.X < _0)
or (hwRound(HHGear^.X) > LAND_WIDTH)) then
@@ -4048,7 +3602,7 @@
break;
// don't port portals or other gear that wouldn't make sense
- if (iterator^.Kind in [gtPortal, gtRope])
+ if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack])
or (iterator^.PortalCounter > 32) then
continue;
@@ -4163,7 +3717,7 @@
// calc gear offset in portal normal vector direction
noffs:= (nx * ox + ny * oy);
- if isBullet and (hwRound(hwAbs(noffs)) >= Gear^.Radius) then
+ if isBullet and (noffs.Round >= Gear^.Radius) then
continue;
// avoid gravity related loops of not really moving gear
@@ -4330,23 +3884,6 @@
iterator^.Friction := iterator^.Y;
end;
- // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
- iterator:= GearsList;
- while iterator <> nil do
- begin
- if (iterator^.Kind <> gtPortal) and ((iterator^.Hedgehog <> CurrentHedgehog)
- or ((iterator^.Message and gmAllStoppable) = 0)) then
- begin
- iterator^.Active:= true;
- if iterator^.dY.QWordValue = _0.QWordValue then
- iterator^.dY.isNegative:= false;
- iterator^.State:= iterator^.State or gstMoving;
- DeleteCI(iterator);
- //inc(iterator^.dY.QWordValue,10);
- end;
- iterator:= iterator^.NextGear
- end;
-
if Gear^.Health > 1 then
dec(Gear^.Health);
end;
@@ -4398,7 +3935,7 @@
Gear^.State := Gear^.State and (not gstMoving);
if (Land[y, x] and lfBouncy <> 0)
- or not CalcSlopeTangent(Gear, x, y, tx, ty, 255)
+ or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255))
or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain
begin
loadNewPortalBall(Gear, true);
@@ -4475,7 +4012,7 @@
iterator := GearsList;
while iterator <> nil do
- begin
+ begin
if (iterator^.Kind = gtPortal) then
if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then
begin
@@ -4493,7 +4030,27 @@
end;
iterator^.PortalCounter:= 0;
iterator := iterator^.NextGear
- end;
+ end;
+
+ if newPortal^.LinkedGear <> nil then
+ begin
+ // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
+ iterator:= GearsList;
+ while iterator <> nil do
+ begin
+ if not (iterator^.Kind in [gtPortal, gtAirAttack]) and ((iterator^.Hedgehog <> CurrentHedgehog)
+ or ((iterator^.Message and gmAllStoppable) = 0)) then
+ begin
+ iterator^.Active:= true;
+ if iterator^.dY.QWordValue = _0.QWordValue then
+ iterator^.dY.isNegative:= false;
+ iterator^.State:= iterator^.State or gstMoving;
+ DeleteCI(iterator);
+ //inc(iterator^.dY.QWordValue,10);
+ end;
+ iterator:= iterator^.NextGear
+ end
+ end
end;
newPortal^.State := newPortal^.State and (not gstCollision);
newPortal^.State := newPortal^.State or gstMoving;
@@ -4796,7 +4353,7 @@
Gear^.Timer:= Gear^.Tag
end;
- if (Gear^.Health = 0) or (HHGear^.Damage <> 0) then
+ if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then
begin
DeleteGear(Gear);
AfterAttack
@@ -4872,7 +4429,7 @@
Gear^.Timer:= Gear^.Tag
end;
- if (Gear^.Health = 0) or (HHGear^.Damage <> 0) or ((HHGear^.Message and gmAttack) <> 0) then
+ if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then
begin
HHGear^.Message:= HHGear^.Message and (not gmAttack);
DeleteGear(Gear);
@@ -5597,3 +5154,84 @@
doStepFallingGear(Gear);
end;
+
+procedure doStepCreeper(Gear: PGear);
+var hogs: PGearArrayS;
+ HHGear: PGear;
+ tdX: hwFloat;
+ dir: LongInt;
+begin
+doStepFallingGear(Gear);
+if Gear^.Timer > 0 then dec(Gear^.Timer);
+// creeper sleep phase
+if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit;
+
+if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear
+else HHGear:= nil;
+
+// creeper boom phase
+if (Gear^.State and gstTmpFlag <> 0) then
+ begin
+ if (Gear^.Timer = 0) then
+ begin
+ doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound);
+ DeleteGear(Gear)
+ end;
+ // ssssss he essssscaped
+ if (Gear^.Timer > 250) and ((HHGear = nil) or
+ (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > 180) and
+ (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then
+ begin
+ Gear^.State:= Gear^.State and (not gstTmpFlag);
+ Gear^.Timer:= 0
+ end;
+ exit
+ end;
+
+// Search out a new target, as target seek time has expired, target is dead, target is out of range, or we didn't have a target
+if (HHGear = nil) or (Gear^.Timer = 0) or
+ (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > Gear^.Angle) and
+ (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle)))
+ then
+ begin
+ hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle);
+ if hogs.size > 1 then
+ Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog
+ else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog
+ else Gear^.Hedgehog:= nil;
+ if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000;
+ exit
+ end;
+
+// we have a target. move the creeper.
+if HHGear <> nil then
+ begin
+ // GOTCHA
+ if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) < 50) and
+ (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then
+ begin
+ // hisssssssssss
+ Gear^.State:= Gear^.State or gstTmpFlag;
+ Gear^.Timer:= 1500;
+ exit
+ end;
+ if (Gear^.State and gstMoving <> 0) then
+ begin
+ Gear^.dY:= _0;
+ Gear^.dX:= _0;
+ end
+ else if (GameTicks and $FF = 0) then
+ begin
+ tdX:= HHGear^.X-Gear^.X;
+ dir:= hwSign(tdX);
+ if not TestCollisionX(Gear, dir) then
+ Gear^.X:= Gear^.X + signAs(_1,tdX);
+ if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then
+ begin
+ Gear^.dX:= SignAs(_0_15, tdX);
+ Gear^.dY:= -_0_3;
+ Gear^.State:= Gear^.State or gstMoving
+ end
+ end;
+ end;
+end;
--- a/hedgewars/SDLh.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/SDLh.pas Thu Aug 30 13:02:19 2012 -0400
@@ -410,12 +410,10 @@
PSDL_Color = ^TSDL_Color;
TSDL_Color = record
- case Byte of
- 0: ( r: Byte;
- g: Byte;
- b: Byte;
- unused: Byte; );
- 1: ( value: LongWord; );
+ r: Byte;
+ g: Byte;
+ b: Byte;
+ unused: Byte;
end;
@@ -829,6 +827,8 @@
TMixMusic = record
end;
+ TPostMix = procedure(udata: pointer; stream: PByte; len: LongInt); cdecl;
+
{* SDL_net *}
TIPAddress = record
host: LongWord;
@@ -959,6 +959,9 @@
function SDL_GL_SetAttribute(attr: TSDL_GLattr; value: LongInt): LongInt; cdecl; external SDLLibName;
procedure SDL_GL_SwapBuffers; cdecl; external SDLLibName;
+procedure SDL_LockAudio; cdecl; external SDLLibName;
+procedure SDL_UnlockAudio; cdecl; external SDLLibName;
+
function SDL_NumJoysticks: LongInt; cdecl; external SDLLibName;
function SDL_JoystickName(idx: LongInt): PChar; cdecl; external SDLLibName;
function SDL_JoystickOpen(idx: LongInt): PSDL_Joystick; cdecl; external SDLLibName;
@@ -1010,6 +1013,7 @@
function Mix_OpenAudio(frequency: LongInt; format: Word; channels: LongInt; chunksize: LongInt): LongInt; cdecl; external SDL_MixerLibName;
procedure Mix_CloseAudio; cdecl; external SDL_MixerLibName;
+function Mix_QuerySpec(frequency: PLongInt; format: PWord; channels: PLongInt): LongInt; cdecl; external SDL_MixerLibName;
function Mix_Volume(channel: LongInt; volume: LongInt): LongInt; cdecl; external SDL_MixerLibName;
function Mix_SetDistance(channel: LongInt; distance: Byte): LongInt; cdecl; external SDL_MixerLibName;
@@ -1037,6 +1041,8 @@
function Mix_FadeInChannelTimed(channel: LongInt; chunk: PMixChunk; loops: LongInt; fadems: LongInt; ticks: LongInt): LongInt; cdecl; external SDL_MixerLibName;
function Mix_FadeOutChannel(channel: LongInt; fadems: LongInt): LongInt; cdecl; external SDL_MixerLibName;
+procedure Mix_SetPostMix( mix_func: TPostMix; arg: pointer); cdecl; external SDL_MixerLibName;
+
(* SDL_image *)
function IMG_Init(flags: LongInt): LongInt; {$IFDEF SDL_IMAGE_NEWER}cdecl; external SDL_ImageLibName;{$ENDIF}
procedure IMG_Quit; {$IFDEF SDL_IMAGE_NEWER}cdecl; external SDL_ImageLibName;{$ENDIF}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/avwrapper.c Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,515 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include "libavformat/avformat.h"
+
+#ifndef AVIO_FLAG_WRITE
+#define AVIO_FLAG_WRITE AVIO_WRONLY
+#endif
+
+static AVFormatContext* g_pContainer;
+static AVOutputFormat* g_pFormat;
+static AVStream* g_pAStream;
+static AVStream* g_pVStream;
+static AVFrame* g_pAFrame;
+static AVFrame* g_pVFrame;
+static AVCodec* g_pACodec;
+static AVCodec* g_pVCodec;
+static AVCodecContext* g_pAudio;
+static AVCodecContext* g_pVideo;
+
+static int g_Width, g_Height;
+static uint32_t g_Frequency, g_Channels;
+static int g_VQuality;
+static AVRational g_Framerate;
+
+static FILE* g_pSoundFile;
+static int16_t* g_pSamples;
+static int g_NumSamples;
+
+/*
+Initially I wrote code for latest ffmpeg, but on Linux (Ubuntu)
+only older version is available from repository. That's why you see here
+all of this #if LIBAVCODEC_VERSION_MAJOR < 54.
+Actually, it may be possible to remove code for newer version
+and use only code for older version.
+*/
+
+#if LIBAVCODEC_VERSION_MAJOR < 54
+#define OUTBUFFER_SIZE 200000
+static uint8_t g_OutBuffer[OUTBUFFER_SIZE];
+#endif
+
+// pointer to function from hwengine (uUtils.pas)
+static void (*AddFileLogRaw)(const char* pString);
+
+static void FatalError(const char* pFmt, ...)
+{
+ const char Buffer[1024];
+ va_list VaArgs;
+
+ va_start(VaArgs, pFmt);
+ vsnprintf(Buffer, 1024, pFmt, VaArgs);
+ va_end(VaArgs);
+
+ AddFileLogRaw("Error in av-wrapper: ");
+ AddFileLogRaw(Buffer);
+ AddFileLogRaw("\n");
+ exit(1);
+}
+
+// Function to be called from libav for logging.
+// Note: libav can call LogCallback from different threads
+// (there is mutex in AddFileLogRaw).
+static void LogCallback(void* p, int Level, const char* pFmt, va_list VaArgs)
+{
+ const char Buffer[1024];
+
+ vsnprintf(Buffer, 1024, pFmt, VaArgs);
+ AddFileLogRaw(Buffer);
+}
+
+static void Log(const char* pFmt, ...)
+{
+ const char Buffer[1024];
+ va_list VaArgs;
+
+ va_start(VaArgs, pFmt);
+ vsnprintf(Buffer, 1024, pFmt, VaArgs);
+ va_end(VaArgs);
+
+ AddFileLogRaw(Buffer);
+}
+
+static void AddAudioStream()
+{
+#if LIBAVFORMAT_VERSION_MAJOR >= 54
+ g_pAStream = avformat_new_stream(g_pContainer, g_pACodec);
+#else
+ g_pAStream = av_new_stream(g_pContainer, 1);
+#endif
+ if(!g_pAStream)
+ {
+ Log("Could not allocate audio stream\n");
+ return;
+ }
+ g_pAStream->id = 1;
+
+ g_pAudio = g_pAStream->codec;
+
+ avcodec_get_context_defaults3(g_pAudio, g_pACodec);
+ g_pAudio->codec_id = g_pACodec->id;
+
+ // put parameters
+ g_pAudio->sample_fmt = AV_SAMPLE_FMT_S16;
+ g_pAudio->sample_rate = g_Frequency;
+ g_pAudio->channels = g_Channels;
+
+ // set quality
+ g_pAudio->bit_rate = 160000;
+
+ // for codecs that support variable bitrate use it, it should be better
+ g_pAudio->flags |= CODEC_FLAG_QSCALE;
+ g_pAudio->global_quality = 1*FF_QP2LAMBDA;
+
+ // some formats want stream headers to be separate
+ if (g_pFormat->flags & AVFMT_GLOBALHEADER)
+ g_pAudio->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+ // open it
+#if LIBAVCODEC_VERSION_MAJOR >= 53
+ if (avcodec_open2(g_pAudio, g_pACodec, NULL) < 0)
+#else
+ if (avcodec_open(g_pAudio, g_pACodec) < 0)
+#endif
+ {
+ Log("Could not open audio codec %s\n", g_pACodec->long_name);
+ return;
+ }
+
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+ if (g_pACodec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)
+#else
+ if (g_pAudio->frame_size == 0)
+#endif
+ g_NumSamples = 4096;
+ else
+ g_NumSamples = g_pAudio->frame_size;
+ g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t));
+ g_pAFrame = avcodec_alloc_frame();
+ if (!g_pAFrame)
+ {
+ Log("Could not allocate frame\n");
+ return;
+ }
+}
+
+// returns non-zero if there is more sound
+static int WriteAudioFrame()
+{
+ if (!g_pAStream)
+ return 0;
+
+ AVPacket Packet = { 0 };
+ av_init_packet(&Packet);
+
+ int NumSamples = fread(g_pSamples, 2*g_Channels, g_NumSamples, g_pSoundFile);
+
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+ AVFrame* pFrame = NULL;
+ if (NumSamples > 0)
+ {
+ g_pAFrame->nb_samples = NumSamples;
+ avcodec_fill_audio_frame(g_pAFrame, g_Channels, AV_SAMPLE_FMT_S16,
+ (uint8_t*)g_pSamples, NumSamples*2*g_Channels, 1);
+ pFrame = g_pAFrame;
+ }
+ // when NumSamples == 0 we still need to call encode_audio2 to flush
+ int got_packet;
+ if (avcodec_encode_audio2(g_pAudio, &Packet, pFrame, &got_packet) != 0)
+ FatalError("avcodec_encode_audio2 failed");
+ if (!got_packet)
+ return 0;
+#else
+ if (NumSamples == 0)
+ return 0;
+ int BufferSize = OUTBUFFER_SIZE;
+ if (g_pAudio->frame_size == 0)
+ BufferSize = NumSamples*g_Channels*2;
+ Packet.size = avcodec_encode_audio(g_pAudio, g_OutBuffer, BufferSize, g_pSamples);
+ if (Packet.size == 0)
+ return 1;
+ if (g_pAudio->coded_frame && g_pAudio->coded_frame->pts != AV_NOPTS_VALUE)
+ Packet.pts = av_rescale_q(g_pAudio->coded_frame->pts, g_pAudio->time_base, g_pAStream->time_base);
+ Packet.flags |= AV_PKT_FLAG_KEY;
+ Packet.data = g_OutBuffer;
+#endif
+
+ // Write the compressed frame to the media file.
+ Packet.stream_index = g_pAStream->index;
+ if (av_interleaved_write_frame(g_pContainer, &Packet) != 0)
+ FatalError("Error while writing audio frame");
+ return 1;
+}
+
+// add a video output stream
+static void AddVideoStream()
+{
+#if LIBAVFORMAT_VERSION_MAJOR >= 54
+ g_pVStream = avformat_new_stream(g_pContainer, g_pVCodec);
+#else
+ g_pVStream = av_new_stream(g_pContainer, 0);
+#endif
+ if (!g_pVStream)
+ FatalError("Could not allocate video stream");
+
+ g_pVideo = g_pVStream->codec;
+
+ avcodec_get_context_defaults3(g_pVideo, g_pVCodec);
+ g_pVideo->codec_id = g_pVCodec->id;
+
+ // put parameters
+ // resolution must be a multiple of two
+ g_pVideo->width = g_Width & ~1; // make even (dimensions should be even)
+ g_pVideo->height = g_Height & ~1; // make even
+ /* time base: this is the fundamental unit of time (in seconds) in terms
+ of which frame timestamps are represented. for fixed-fps content,
+ timebase should be 1/framerate and timestamp increments should be
+ identically 1. */
+ g_pVideo->time_base.den = g_Framerate.num;
+ g_pVideo->time_base.num = g_Framerate.den;
+ //g_pVideo->gop_size = 12; /* emit one intra frame every twelve frames at most */
+ g_pVideo->pix_fmt = PIX_FMT_YUV420P;
+
+ // set quality
+ if (g_VQuality > 100)
+ g_pVideo->bit_rate = g_VQuality;
+ else
+ {
+ g_pVideo->flags |= CODEC_FLAG_QSCALE;
+ g_pVideo->global_quality = g_VQuality*FF_QP2LAMBDA;
+ }
+
+ // some formats want stream headers to be separate
+ if (g_pFormat->flags & AVFMT_GLOBALHEADER)
+ g_pVideo->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+#if LIBAVCODEC_VERSION_MAJOR < 54
+ // for some versions of ffmpeg x264 options must be set explicitly
+ if (strcmp(g_pVCodec->name, "libx264") == 0)
+ {
+ g_pVideo->coder_type = FF_CODER_TYPE_AC;
+ g_pVideo->flags |= CODEC_FLAG_LOOP_FILTER;
+ g_pVideo->crf = 23;
+ g_pVideo->thread_count = 3;
+ g_pVideo->me_cmp = FF_CMP_CHROMA;
+ g_pVideo->partitions = X264_PART_I8X8 | X264_PART_I4X4 | X264_PART_P8X8 | X264_PART_B8X8;
+ g_pVideo->me_method = ME_HEX;
+ g_pVideo->me_subpel_quality = 7;
+ g_pVideo->me_range = 16;
+ g_pVideo->gop_size = 250;
+ g_pVideo->keyint_min = 25;
+ g_pVideo->scenechange_threshold = 40;
+ g_pVideo->i_quant_factor = 0.71;
+ g_pVideo->b_frame_strategy = 1;
+ g_pVideo->qcompress = 0.6;
+ g_pVideo->qmin = 10;
+ g_pVideo->qmax = 51;
+ g_pVideo->max_qdiff = 4;
+ g_pVideo->max_b_frames = 3;
+ g_pVideo->refs = 3;
+ g_pVideo->directpred = 1;
+ g_pVideo->trellis = 1;
+ g_pVideo->flags2 = CODEC_FLAG2_BPYRAMID | CODEC_FLAG2_MIXED_REFS | CODEC_FLAG2_WPRED | CODEC_FLAG2_8X8DCT | CODEC_FLAG2_FASTPSKIP;
+ g_pVideo->weighted_p_pred = 2;
+ }
+#endif
+
+ // open the codec
+#if LIBAVCODEC_VERSION_MAJOR >= 53
+ AVDictionary* pDict = NULL;
+ if (strcmp(g_pVCodec->name, "libx264") == 0)
+ av_dict_set(&pDict, "preset", "medium", 0);
+
+ if (avcodec_open2(g_pVideo, g_pVCodec, &pDict) < 0)
+#else
+ if (avcodec_open(g_pVideo, g_pVCodec) < 0)
+#endif
+ FatalError("Could not open video codec %s", g_pVCodec->long_name);
+
+ g_pVFrame = avcodec_alloc_frame();
+ if (!g_pVFrame)
+ FatalError("Could not allocate frame");
+
+ g_pVFrame->linesize[0] = g_Width;
+ g_pVFrame->linesize[1] = g_Width/2;
+ g_pVFrame->linesize[2] = g_Width/2;
+ g_pVFrame->linesize[3] = 0;
+}
+
+static int WriteFrame(AVFrame* pFrame)
+{
+ double AudioTime, VideoTime;
+
+ // write interleaved audio frame
+ if (g_pAStream)
+ {
+ VideoTime = (double)g_pVStream->pts.val*g_pVStream->time_base.num/g_pVStream->time_base.den;
+ do
+ AudioTime = (double)g_pAStream->pts.val*g_pAStream->time_base.num/g_pAStream->time_base.den;
+ while (AudioTime < VideoTime && WriteAudioFrame());
+ }
+
+ if (!g_pVStream)
+ return 0;
+
+ AVPacket Packet;
+ av_init_packet(&Packet);
+ Packet.data = NULL;
+ Packet.size = 0;
+
+ g_pVFrame->pts++;
+ if (g_pFormat->flags & AVFMT_RAWPICTURE)
+ {
+ /* raw video case. The API will change slightly in the near
+ future for that. */
+ Packet.flags |= AV_PKT_FLAG_KEY;
+ Packet.stream_index = g_pVStream->index;
+ Packet.data = (uint8_t*)pFrame;
+ Packet.size = sizeof(AVPicture);
+
+ if (av_interleaved_write_frame(g_pContainer, &Packet) != 0)
+ FatalError("Error while writing video frame");
+ return 0;
+ }
+ else
+ {
+#if LIBAVCODEC_VERSION_MAJOR >= 54
+ int got_packet;
+ if (avcodec_encode_video2(g_pVideo, &Packet, pFrame, &got_packet) < 0)
+ FatalError("avcodec_encode_video2 failed");
+ if (!got_packet)
+ return 0;
+
+ if (Packet.pts != AV_NOPTS_VALUE)
+ Packet.pts = av_rescale_q(Packet.pts, g_pVideo->time_base, g_pVStream->time_base);
+ if (Packet.dts != AV_NOPTS_VALUE)
+ Packet.dts = av_rescale_q(Packet.dts, g_pVideo->time_base, g_pVStream->time_base);
+#else
+ Packet.size = avcodec_encode_video(g_pVideo, g_OutBuffer, OUTBUFFER_SIZE, pFrame);
+ if (Packet.size < 0)
+ FatalError("avcodec_encode_video failed");
+ if (Packet.size == 0)
+ return 0;
+
+ if( g_pVideo->coded_frame->pts != AV_NOPTS_VALUE)
+ Packet.pts = av_rescale_q(g_pVideo->coded_frame->pts, g_pVideo->time_base, g_pVStream->time_base);
+ if( g_pVideo->coded_frame->key_frame )
+ Packet.flags |= AV_PKT_FLAG_KEY;
+ Packet.data = g_OutBuffer;
+#endif
+ // write the compressed frame in the media file
+ Packet.stream_index = g_pVStream->index;
+ if (av_interleaved_write_frame(g_pContainer, &Packet) != 0)
+ FatalError("Error while writing video frame");
+
+ return 1;
+ }
+}
+
+void AVWrapper_WriteFrame(uint8_t* pY, uint8_t* pCb, uint8_t* pCr)
+{
+ g_pVFrame->data[0] = pY;
+ g_pVFrame->data[1] = pCb;
+ g_pVFrame->data[2] = pCr;
+ WriteFrame(g_pVFrame);
+}
+
+void AVWrapper_Init(
+ void (*pAddFileLogRaw)(const char*),
+ const char* pFilename,
+ const char* pDesc,
+ const char* pSoundFile,
+ const char* pFormatName,
+ const char* pVCodecName,
+ const char* pACodecName,
+ int Width, int Height,
+ int FramerateNum, int FramerateDen,
+ int VQuality)
+{
+ AddFileLogRaw = pAddFileLogRaw;
+ av_log_set_callback( &LogCallback );
+
+ g_Width = Width;
+ g_Height = Height;
+ g_Framerate.num = FramerateNum;
+ g_Framerate.den = FramerateDen;
+ g_VQuality = VQuality;
+
+ // initialize libav and register all codecs and formats
+ av_register_all();
+
+ // find format
+ g_pFormat = av_guess_format(pFormatName, NULL, NULL);
+ if (!g_pFormat)
+ FatalError("Format \"%s\" was not found", pFormatName);
+
+ // allocate the output media context
+ g_pContainer = avformat_alloc_context();
+ if (!g_pContainer)
+ FatalError("Could not allocate output context");
+
+ g_pContainer->oformat = g_pFormat;
+
+ // store description of file
+ av_dict_set(&g_pContainer->metadata, "comment", pDesc, 0);
+
+ // append extesnion to filename
+ char ext[16];
+ strncpy(ext, g_pFormat->extensions, 16);
+ ext[15] = 0;
+ ext[strcspn(ext,",")] = 0;
+ snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), "%s.%s", pFilename, ext);
+
+ // find codecs
+ g_pVCodec = avcodec_find_encoder_by_name(pVCodecName);
+ g_pACodec = avcodec_find_encoder_by_name(pACodecName);
+
+ // add audio and video stream to container
+ g_pVStream = NULL;
+ g_pAStream = NULL;
+
+ if (g_pVCodec)
+ AddVideoStream();
+ else
+ Log("Video codec \"%s\" was not found; video will be ignored.\n", pVCodecName);
+
+ if (g_pACodec)
+ {
+ g_pSoundFile = fopen(pSoundFile, "rb");
+ if (g_pSoundFile)
+ {
+ fread(&g_Frequency, 4, 1, g_pSoundFile);
+ fread(&g_Channels, 4, 1, g_pSoundFile);
+ AddAudioStream();
+ }
+ else
+ Log("Could not open %s\n", pSoundFile);
+ }
+ else
+ Log("Audio codec \"%s\" was not found; audio will be ignored.\n", pACodecName);
+
+ if (!g_pAStream && !g_pVStream)
+ FatalError("No video, no audio, aborting...");
+
+ // write format info to log
+ av_dump_format(g_pContainer, 0, g_pContainer->filename, 1);
+
+ // open the output file, if needed
+ if (!(g_pFormat->flags & AVFMT_NOFILE))
+ {
+ if (avio_open(&g_pContainer->pb, g_pContainer->filename, AVIO_FLAG_WRITE) < 0)
+ FatalError("Could not open output file (%s)", g_pContainer->filename);
+ }
+
+ // write the stream header, if any
+ avformat_write_header(g_pContainer, NULL);
+
+ g_pVFrame->pts = -1;
+}
+
+void AVWrapper_Close()
+{
+ // output buffered frames
+ if (g_pVCodec->capabilities & CODEC_CAP_DELAY)
+ while( WriteFrame(NULL) );
+ // output any remaining audio
+ while( WriteAudioFrame() );
+
+ // write the trailer, if any.
+ av_write_trailer(g_pContainer);
+
+ // close the output file
+ if (!(g_pFormat->flags & AVFMT_NOFILE))
+ avio_close(g_pContainer->pb);
+
+ // free everything
+ if (g_pVStream)
+ {
+ avcodec_close(g_pVideo);
+ av_free(g_pVideo);
+ av_free(g_pVStream);
+ av_free(g_pVFrame);
+ }
+ if (g_pAStream)
+ {
+ avcodec_close(g_pAudio);
+ av_free(g_pAudio);
+ av_free(g_pAStream);
+ av_free(g_pAFrame);
+ av_free(g_pSamples);
+ fclose(g_pSoundFile);
+ }
+
+ av_free(g_pContainer);
+}
--- a/hedgewars/hwengine.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/hwengine.pas Thu Aug 30 13:02:19 2012 -0400
@@ -30,9 +30,11 @@
{$ENDIF}
uses SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler, uSound,
- uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uRandom, uLandTexture, uCollisions,
+ uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uLandTexture, uCollisions,
SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted
- {$IFDEF SDL13}, uTouch{$ENDIF}{$IFDEF ANDROID}, GLUnit{$ENDIF};
+ {$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF}
+ {$IFDEF SDL13}, uTouch{$ENDIF}{$IFDEF ANDROID}, GLUnit{$ENDIF}, uAILandMarks;
+
{$IFDEF HWLIBRARY}
procedure initEverything(complete:boolean);
@@ -57,6 +59,9 @@
gsLandGen:
begin
GenMap;
+ uLandTexture.initModule;
+ UpdateLandTexture(0, LAND_WIDTH, 0, LAND_HEIGHT, false);
+ uAILandMarks.initModule;
ParseCommand('sendlanddigest', true);
GameState:= gsStart;
end;
@@ -81,7 +86,7 @@
end;
gsConfirm, gsGame:
begin
- DrawWorld(Lag); // never place between ProcessKbd and DoGameTick - bugs due to /put cmd and isCursorVisible
+ DrawWorld(Lag);
DoGameTick(Lag);
ProcessVisualGears(Lag);
end;
@@ -101,18 +106,27 @@
SwapBuffers;
+{$IFDEF USE_VIDEO_RECORDING}
+ if flagPrerecording then
+ SaveCameraPosition;
+{$ENDIF}
+
if flagMakeCapture then
begin
flagMakeCapture:= false;
{$IFDEF PAS2C}
- s:= 'hw';
+ s:= '/Screenshots/hw';
{$ELSE}
- s:= 'hw_' + FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now()) + inttostr(GameTicks);
+ s:= '/Screenshots/hw_' + FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now()) + inttostr(GameTicks);
{$ENDIF}
+ // flash
playSound(sndShutter);
-
- if MakeScreenshot(s) then
+ ScreenFade:= sfFromWhite;
+ ScreenFadeValue:= sfMax;
+ ScreenFadeSpeed:= 5;
+
+ if MakeScreenshot(s, 1) then
WriteLnToConsole('Screenshot saved: ' + s)
else
begin
@@ -261,6 +275,39 @@
end;
end;
+{$IFDEF USE_VIDEO_RECORDING}
+procedure RecorderMainLoop;
+var oldGameTicks, oldRealTicks, newGameTicks, newRealTicks: LongInt;
+begin
+ if not BeginVideoRecording() then
+ exit;
+ DoTimer(0); // gsLandGen -> gsStart
+ DoTimer(0); // gsStart -> gsGame
+
+ if not LoadNextCameraPosition(newRealTicks, newGameTicks) then
+ exit;
+ fastScrolling:= true;
+ DoGameTick(newGameTicks);
+ fastScrolling:= false;
+ oldRealTicks:= 0;
+ oldGameTicks:= newGameTicks;
+
+ while LoadNextCameraPosition(newRealTicks, newGameTicks) do
+ begin
+ IPCCheckSock();
+ DoGameTick(newGameTicks - oldGameTicks);
+ if GameState = gsExit then
+ break;
+ ProcessVisualGears(newRealTicks - oldRealTicks);
+ DrawWorld(newRealTicks - oldRealTicks);
+ EncodeFrame();
+ oldRealTicks:= newRealTicks;
+ oldGameTicks:= newGameTicks;
+ end;
+ StopVideoRecording();
+end;
+{$ENDIF}
+
///////////////
procedure Game{$IFDEF HWLIBRARY}(gameArgs: PPChar); cdecl; export{$ENDIF};
var p: TPathType;
@@ -327,11 +374,18 @@
SDLTry(TTF_Init() <> -1, true);
WriteLnToConsole(msgOK);
- // show main window
- if cFullScreen then
- ParseCommand('fullscr 1', true)
+{$IFDEF USE_VIDEO_RECORDING}
+ if GameType = gmtRecord then
+ InitOffscreenOpenGL()
else
- ParseCommand('fullscr 0', true);
+{$ENDIF}
+ begin
+ // show main window
+ if cFullScreen then
+ ParseCommand('fullscr 1', true)
+ else
+ ParseCommand('fullscr 0', true);
+ end;
ControllerInit(); // has to happen before InitKbdKeyTable to map keys
InitKbdKeyTable();
@@ -368,12 +422,22 @@
InitTeams();
AssignStores();
+
+ if GameType = gmtRecord then
+ SetSound(false);
+
InitSound();
isDeveloperMode:= false;
TryDo(InitStepsFlags = cifAllInited, 'Some parameters not set (flags = ' + inttostr(InitStepsFlags) + ')', true);
ParseCommand('rotmask', true);
- MainLoop();
+
+{$IFDEF USE_VIDEO_RECORDING}
+ if GameType = gmtRecord then
+ RecorderMainLoop()
+ else
+{$ENDIF}
+ MainLoop();
// clean up all the memory allocated
freeEverything(true);
@@ -412,9 +476,7 @@
//uLandGraphics does not need initialization
//uLandObjects does not need initialization
//uLandTemplates does not need initialization
- uLandTexture.initModule;
//uLocale does not need initialization
- uRandom.initModule;
uScript.initModule;
uSound.initModule;
uStats.initModule;
@@ -432,6 +494,7 @@
begin
WriteLnToConsole('Freeing resources...');
uAI.freeModule;
+ uAILandMarks.freeModule;
uAIMisc.freeModule; //stub
uCaptions.freeModule;
uWorld.freeModule;
@@ -441,7 +504,7 @@
uStats.freeModule; //stub
uSound.freeModule;
uScript.freeModule;
- uRandom.freeModule; //stub
+ //uRandom does not need to be freed
//uLocale does not need to be freed
//uLandTemplates does not need to be freed
uLandTexture.freeModule;
@@ -456,6 +519,7 @@
//uAIAmmoTests does not need to be freed
//uAIActions does not need to be freed
uStore.freeModule;
+{$IFDEF USE_VIDEO_RECORDING}uVideoRec.freeModule;{$ENDIF}
end;
uIO.freeModule;
@@ -529,11 +593,14 @@
else
if (ParamCount = 3) and ((ParamStr(3) = '--stats-only') or (ParamStr(3) = 'landpreview')) then
internalSetGameTypeLandPreviewFromParameters()
+ else if ParamCount = cDefaultParamNum then
+ internalStartGameWithParameters()
+{$IFDEF USE_VIDEO_RECORDING}
+ else if ParamCount = cVideorecParamNum then
+ internalStartVideoRecordingWithParameters()
+{$ENDIF}
else
- if (ParamCount = cDefaultParamNum) then
- internalStartGameWithParameters()
- else
- playReplayFileWithParameters();
+ playReplayFileWithParameters();
end;
////////////////////////////////////////////////////////////////////////////////
--- a/hedgewars/pas2cSystem.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/pas2cSystem.pas Thu Aug 30 13:02:19 2012 -0400
@@ -38,6 +38,7 @@
PPChar = ^Pchar;
PByte = ^Byte;
+ PWord = ^Word;
PLongInt = ^LongInt;
PLongWord = ^LongWord;
PInteger = ^Integer;
--- a/hedgewars/uAI.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAI.pas Thu Aug 30 13:02:19 2012 -0400
@@ -31,7 +31,7 @@
implementation
uses uConsts, SDLh, uAIMisc, uAIAmmoTests, uAIActions,
uAmmos, SysUtils{$IFNDEF USE_SDLTHREADS} {$IFDEF UNIX}, cthreads{$ENDIF} {$ENDIF}, uTypes,
- uVariables, uCommands, uUtils, uDebug;
+ uVariables, uCommands, uUtils, uDebug, uAILandMarks;
var BestActions: TActions;
CanUseAmmo: array [TAmmoType] of boolean;
@@ -178,9 +178,12 @@
begin
AddAction(BestActions, aia_attack, aim_push, 350 + random(200), 0, 0);
AddAction(BestActions, aia_attack, aim_release, 1, 0, 0);
-
- AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0);
- AddAction(BestActions, aia_Down, aim_release, 32, 0, 0);
+
+ if abs(ap.Angle) > 32 then
+ begin
+ AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0);
+ AddAction(BestActions, aia_Down, aim_release, 32, 0, 0);
+ end;
AddAction(BestActions, aia_waitAngle, ap.Angle, 250, 0, 0);
AddAction(BestActions, aia_attack, aim_push, 1, 0, 0);
@@ -191,7 +194,14 @@
AddAction(BestActions, aia_attack, aim_push, 650 + random(300), 0, 0);
AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0);
end;
-
+
+ if (Ammoz[a].Ammo.Propz and ammoprop_Track) <> 0 then
+ begin
+ AddAction(BestActions, aia_waitAmmoXY, 0, 12, ap.ExplX, ap.ExplY);
+ AddAction(BestActions, aia_attack, aim_push, 1, 0, 0);
+ AddAction(BestActions, aia_attack, aim_release, 7, 0, 0);
+ end;
+
if ap.ExplR > 0 then
AddAction(BestActions, aia_AwareExpl, ap.ExplR, 10, ap.ExplX, ap.ExplY);
end
@@ -205,7 +215,7 @@
end;
procedure Walk(Me: PGear; var Actions: TActions);
-const FallPixForBranching = cHHRadius * 2 + 8;
+const FallPixForBranching = cHHRadius;
var
ticks, maxticks, steps, tmp: Longword;
BaseRate, BestRate, Rate: integer;
@@ -267,7 +277,9 @@
break;
if (BotLevel < 5) and (GoInfo.JumpType = jmpHJump) then // hjump support
- if Push(ticks, Actions, AltMe, Me^.Message) then
+ // check if we could go backwards and maybe ljump over a gap after this hjump
+ if Push(ticks, Actions, AltMe, Me^.Message xor 3) then
+ begin
with Stack.States[Pred(Stack.Count)] do
begin
if Me^.dX.isNegative then
@@ -283,11 +295,20 @@
else
AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0);
end;
+ // but first check walking forward
+ Push(ticks, Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message)
+ end;
if (BotLevel < 3) and (GoInfo.JumpType = jmpLJump) then // ljump support
begin
- // push current position so we proceed from it after checking jump opportunities
+ // at final check where we go after jump walking backward
+ if Push(ticks, Actions, AltMe, Me^.Message xor 3) then
+ with Stack.States[Pred(Stack.Count)] do
+ AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0);
+
+ // push current position so we proceed from it after checking jump+forward walk opportunities
if CanGo then Push(ticks, Actions, Me^, Me^.Message);
- // first check where we go after jump
+
+ // first check where we go after jump walking forward
if Push(ticks, Actions, AltMe, Me^.Message) then
with Stack.States[Pred(Stack.Count)] do
AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0);
@@ -310,8 +331,16 @@
end
else if Rate < BestRate then
break;
+
if ((Me^.State and gstAttacked) = 0) and ((steps mod 4) = 0) then
+ begin
+ if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWasHere) then
+ break;
+ addMark(hwRound(Me^.X), hwRound(Me^.Y), markWasHere);
+
TestAmmos(Actions, Me, true);
+ end;
+
if GoInfo.FallPix >= FallPixForBranching then
Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right
end {while};
@@ -408,7 +437,7 @@
end
end;
-PGear(Me)^.State:= PGear(Me)^.State and not gstHHThinking;
+PGear(Me)^.State:= PGear(Me)^.State and (not gstHHThinking);
Think:= 0;
InterlockedDecrement(hasThread)
end;
@@ -419,7 +448,9 @@
or isInMultiShoot then
exit;
-//DeleteCI(Me); // this might break demo
+//DeleteCI(Me); // this will break demo/netplay
+clearAllMarks;
+
Me^.State:= Me^.State or gstHHThinking;
Me^.Message:= 0;
@@ -476,12 +507,11 @@
end else
begin
- (*
- if not scoreShown then
+ {if not scoreShown then
begin
if BestActions.Score > 0 then ParseCommand('/say Expected score = ' + inttostr(BestActions.Score div 1024), true);
scoreShown:= true
- end;*)
+ end;}
ProcessAction(BestActions, Gear)
end
else if ((GameTicks - StartTicks) > cMaxAIThinkTime)
--- a/hedgewars/uAIActions.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAIActions.pas Thu Aug 30 13:02:19 2012 -0400
@@ -44,6 +44,7 @@
aia_Wait = $8009;
aia_Put = $800A;
aia_waitAngle = $800B;
+ aia_waitAmmoXY = $800C;
aim_push = $8000;
aim_release = $8001;
@@ -115,19 +116,19 @@
procedure AddAction(var Actions: TActions; Action: Longword; Param: LongInt; TimeDelta: Longword; X, Y: LongInt);
begin
-with Actions do
- begin
- actions[Count].Action:= Action;
- actions[Count].Param:= Param;
- actions[Count].X:= X;
- actions[Count].Y:= Y;
- if Count > 0 then
- actions[Count].Time:= TimeDelta
- else
- actions[Count].Time:= GameTicks + TimeDelta;
- inc(Count);
- TryDo(Count < MAXACTIONS, 'AI: actions overflow', true);
- end
+if Actions.Count < MAXACTIONS then
+ with Actions do
+ begin
+ actions[Count].Action:= Action;
+ actions[Count].Param:= Param;
+ actions[Count].X:= X;
+ actions[Count].Y:= Y;
+ if Count > 0 then
+ actions[Count].Time:= TimeDelta
+ else
+ actions[Count].Time:= GameTicks + TimeDelta;
+ inc(Count);
+ end
end;
procedure CheckHang(Me: PGear);
@@ -234,6 +235,10 @@
aia_waitAngle:
if Me^.Angle <> Abs(Param) then exit;
+
+ aia_waitAmmoXY:
+ if (CurAmmoGear <> nil) and ((hwRound(CurAmmoGear^.X) <> X) or (hwRound(CurAmmoGear^.Y) <> Y)) then exit;
+
end
else
begin
--- a/hedgewars/uAIAmmoTests.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAIAmmoTests.pas Thu Aug 30 13:02:19 2012 -0400
@@ -47,6 +47,7 @@
function TestBaseballBat(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
function TestFirePunch(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
function TestWhip(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestKamikaze(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
function TestAirAttack(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
function TestTeleport(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
function TestHammer(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
@@ -84,8 +85,8 @@
//(proc: @TestTeleport; flags: amtest_OnTurn), // amTeleport
(proc: nil; flags: 0), // amSwitch
(proc: @TestMortar; flags: 0), // amMortar
- (proc: nil; flags: 0), // amKamikaze
- (proc: @TestCake; flags: 0), // amCake
+ (proc: @TestKamikaze; flags: 0), // amKamikaze
+ (proc: @TestCake; flags: amtest_OnTurn or amtest_NoTarget), // amCake
(proc: nil; flags: 0), // amSeduction
(proc: @TestWatermelon; flags: 0), // amWatermelon
(proc: nil; flags: 0), // amHellishBomb
@@ -122,7 +123,7 @@
const BadTurn = Low(LongInt) div 4;
implementation
-uses uAIMisc, uVariables, uUtils;
+uses uAIMisc, uVariables, uUtils, uGearsHandlers, uCollisions;
function Metric(x1, y1, x2, y2: LongInt): LongInt; inline;
begin
@@ -167,7 +168,7 @@
EX:= trunc(x);
EY:= trunc(y);
- if Me^.Hedgehog^.BotLevel = 1 then
+ if Level = 1 then
value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
else value:= RateExplosion(Me, EX, EY, 101);
if value = 0 then
@@ -330,7 +331,7 @@
EX:= trunc(x);
EY:= trunc(y);
if t < 50 then
- if Me^.Hedgehog^.BotLevel = 1 then
+ if Level = 1 then
Score:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
else Score:= RateExplosion(Me, EX, EY, 101)
else
@@ -361,12 +362,12 @@
t: LongInt;
begin
valueResult:= BadTurn;
-TestTime:= 0;
+TestTime:= 500;
ap.ExplR:= 0;
meX:= hwFloat2Float(Me^.X);
meY:= hwFloat2Float(Me^.Y);
repeat
- inc(TestTime, 1000);
+ inc(TestTime, 900);
// Try to overshoot slightly, seems to pay slightly better dividends in terms of hitting cluster
if meX<Targ.X then
Vx:= ((Targ.X+10) - meX) / (TestTime + tDelta)
@@ -398,14 +399,14 @@
begin
ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level));
ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15);
- ap.Time:= TestTime;
+ ap.Time:= TestTime div 1000 * 1000;
ap.ExplR:= 90;
ap.ExplX:= EX;
ap.ExplY:= EY;
valueResult:= Score
end;
end
-until (TestTime = 4000);
+until (TestTime = 4100);
TestClusterBomb:= valueResult
end;
@@ -418,12 +419,12 @@
t: LongInt;
begin
valueResult:= BadTurn;
-TestTime:= 0;
+TestTime:= 500;
ap.ExplR:= 0;
meX:= hwFloat2Float(Me^.X);
meY:= hwFloat2Float(Me^.Y);
repeat
- inc(TestTime, 1000);
+ inc(TestTime, 900);
Vx:= (Targ.X - meX) / (TestTime + tDelta);
Vy:= cGravityf * ((TestTime + tDelta) div 2) - ((Targ.Y-50) - meY) / (TestTime + tDelta);
r:= sqr(Vx)+sqr(Vy);
@@ -452,14 +453,14 @@
begin
ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random(Level));
ap.Power:= trunc(sqrt(r) * cMaxPower) + AIrndSign(random(Level) * 15);
- ap.Time:= TestTime;
+ ap.Time:= TestTime div 1000 * 1000;
ap.ExplR:= 300;
ap.ExplX:= EX;
ap.ExplY:= EY;
valueResult:= Score
end;
end
-until (TestTime = 4000);
+until (TestTime = 4100);
TestWatermelon:= valueResult
end;
@@ -600,19 +601,19 @@
d: Longword;
fallDmg, valueResult: LongInt;
begin
-if Me^.Hedgehog^.BotLevel > 3 then exit(BadTurn);
+if Level > 3 then exit(BadTurn);
dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
Level:= Level; // avoid compiler hint
ap.ExplR:= 0;
ap.Time:= 0;
ap.Power:= 1;
+
x:= hwFloat2Float(Me^.X);
y:= hwFloat2Float(Me^.Y);
+
if Abs(trunc(x) - Targ.X) + Abs(trunc(y) - Targ.Y) < 40 then
- begin
- TestDesertEagle:= BadTurn;
exit(BadTurn);
- end;
+
t:= 2 / sqrt(sqr(Targ.X - x)+sqr(Targ.Y-y));
Vx:= (Targ.X - x) * t;
Vy:= (Targ.Y - y) * t;
@@ -650,7 +651,7 @@
d: Longword;
fallDmg, valueResult: LongInt;
begin
-if Me^.Hedgehog^.BotLevel > 3 then exit(BadTurn);
+if Level > 3 then exit(BadTurn);
dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
Level:= Level; // avoid compiler hint
ap.ExplR:= 0;
@@ -700,28 +701,28 @@
x, y, trackFall: LongInt;
dx, dy: real;
begin
- if Me^.Hedgehog^.BotLevel < 3 then trackFall:= afTrackFall
+ if Level < 3 then trackFall:= afTrackFall
else trackFall:= 0;
- Level:= Level; // avoid compiler hint
+
ap.ExplR:= 0;
ap.Time:= 0;
ap.Power:= 1;
x:= hwRound(Me^.X);
y:= hwRound(Me^.Y);
- a:= 0;
+ a:= cMaxAngle div 2;
valueResult:= 0;
- while a <= cMaxAngle div 2 do
+ while a >= 0 do
begin
dx:= sin(a / cMaxAngle * pi) * 0.5;
dy:= cos(a / cMaxAngle * pi) * 0.5;
- v1:= RateShove(Me, x - 10, y
- , 33, 30, 115
+ v1:= RateShove(Me, x - 10, y + 2
+ , 32, 30, 115
, -dx, -dy, trackFall);
- v2:= RateShove(Me, x + 10, y
- , 33, 30, 115
+ v2:= RateShove(Me, x + 10, y + 2
+ , 32, 30, 115
, dx, -dy, trackFall);
if (v1 > valueResult) or (v2 > valueResult) then
if (v2 > v1)
@@ -736,7 +737,7 @@
valueResult:= v1
end;
- a:= a + 15 + random(cMaxAngle div 16)
+ a:= a - 15 - random(cMaxAngle div 16)
end;
if valueResult <= 0 then
@@ -749,24 +750,24 @@
var valueResult, v1, v2, i: LongInt;
x, y, trackFall: LongInt;
begin
- if Me^.Hedgehog^.BotLevel = 1 then trackFall:= afTrackFall
+ if Level = 1 then trackFall:= afTrackFall
else trackFall:= 0;
- Level:= Level; // avoid compiler hint
+
ap.ExplR:= 0;
ap.Time:= 0;
ap.Power:= 1;
x:= hwRound(Me^.X);
- y:= hwRound(Me^.Y);
+ y:= hwRound(Me^.Y) + 4;
v1:= 0;
for i:= 0 to 8 do
begin
- v1:= v1 + RateShove(Me, x - 10, y - 10 * i
- , 18, 30, 40
+ v1:= v1 + RateShove(Me, x - 5, y - 10 * i
+ , 19, 30, 40
, -0.45, -0.9, trackFall or afSetSkip);
end;
- v1:= v1 + RateShove(Me, x - 10, y - 90
- , 18, 30, 40
+ v1:= v1 + RateShove(Me, x - 5, y - 90
+ , 19, 30, 40
, -0.45, -0.9, trackFall);
@@ -774,12 +775,12 @@
v2:= 0;
for i:= 0 to 8 do
begin
- v2:= v2 + RateShove(Me, x + 10, y - 10 * i
- , 18, 30, 40
+ v2:= v2 + RateShove(Me, x + 5, y - 10 * i
+ , 19, 30, 40
, 0.45, -0.9, trackFall or afSetSkip);
end;
- v2:= v2 + RateShove(Me, x + 10, y - 90
- , 18, 30, 40
+ v2:= v2 + RateShove(Me, x + 5, y - 90
+ , 19, 30, 40
, 0.45, -0.9, trackFall);
if (v2 > v1)
@@ -805,9 +806,9 @@
var valueResult, v1, v2: LongInt;
x, y, trackFall: LongInt;
begin
- if Me^.Hedgehog^.BotLevel = 1 then trackFall:= afTrackFall
+ if Level = 1 then trackFall:= afTrackFall
else trackFall:= 0;
- Level:= Level; // avoid compiler hint
+
ap.ExplR:= 0;
ap.Time:= 0;
ap.Power:= 1;
@@ -818,19 +819,19 @@
{first RateShove checks farthermost of two whip's AmmoShove attacks
to encourage distant attacks (damaged hog is excluded from view of second
RateShove call)}
- v1:= RateShove(Me, x - 15, y
+ v1:= RateShove(Me, x - 13, y
, 30, 30, 25
, -1, -0.8, trackFall or afSetSkip);
v1:= v1 +
- RateShove(Me, x, y
+ RateShove(Me, x - 2, y
, 30, 30, 25
, -1, -0.8, trackFall);
// now try opposite direction
- v2:= RateShove(Me, x + 15, y
+ v2:= RateShove(Me, x + 13, y
, 30, 30, 25
, 1, -0.8, trackFall or afSetSkip);
v2:= v2 +
- RateShove(Me, x, y
+ RateShove(Me, x + 2, y
, 30, 30, 25
, 1, -0.8, trackFall);
@@ -854,6 +855,90 @@
TestWhip:= valueResult;
end;
+function TestKamikaze(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
+const step = 8;
+var valueResult, i, v, tx: LongInt;
+ trackFall: LongInt;
+ t, d, x, y, dx, dy, cx: real;
+begin
+ ap.ExplR:= 0;
+ ap.Time:= 0;
+ ap.Power:= 1;
+
+ if Level = 1 then
+ trackFall:= afTrackFall
+ else if Level = 2 then
+ trackFall:= 0
+ else
+ exit(BadTurn);
+
+ valueResult:= 0;
+ v:= 0;
+
+ x:= hwFloat2Float(Me^.X);
+ y:= hwFloat2Float(Me^.Y);
+ d:= sqrt(sqr(Targ.X - x) + sqr(Targ.Y - y));
+ if d < 10 then
+ begin
+ dx:= 0;
+ dy:= 8;
+ ap.Angle:= 2048
+ end
+ else
+ begin
+ t:= step / d;
+ dx:= (Targ.X - x) * t;
+ dy:= (Targ.Y - y) * t;
+
+ ap.Angle:= DxDy2AttackAnglef(dx, -dy)
+ end;
+
+ if dx >= 0 then cx:= 0.45 else cx:= -0.45;
+
+ for i:= 0 to 512 div step - 2 do
+ begin
+ valueResult:= valueResult +
+ RateShove(Me, trunc(x), trunc(y)
+ , 30, 30, 25
+ , cx, -0.9, trackFall or afSetSkip);
+
+ x:= x + dx;
+ y:= y + dy;
+ end;
+ if dx = 0 then
+ begin
+ x:= hwFloat2Float(Me^.X);
+ y:= hwFloat2Float(Me^.Y);
+ tx:= trunc(x);
+ v:= RateShove(Me, tx, trunc(y)
+ , 30, 30, 25
+ , -cx, -0.9, trackFall);
+ for i:= 1 to 512 div step - 2 do
+ begin
+ y:= y + dy;
+ v:= v +
+ RateShove(Me, tx, trunc(y)
+ , 30, 30, 25
+ , -cx, -0.9, trackFall or afSetSkip);
+ end
+ end;
+ if v > valueResult then
+ begin
+ ap.Angle:= -2048;
+ valueResult:= v
+ end;
+
+ v:= RateShove(Me, trunc(x), trunc(y)
+ , 30, 30, 25
+ , cx, -0.9, trackFall);
+ valueResult:= valueResult + v - KillScore * friendlyfactor div 100 * 1024;
+
+ if v < 65536 then
+ inc(valueResult, RateExplosion(Me, trunc(x), trunc(y), 30));
+
+ TestKamikaze:= valueResult;
+end;
+
function TestHammer(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
var rate: LongInt;
begin
@@ -983,9 +1068,24 @@
end;
-function checkCakeWalk(Gear: PGear): LongInt;
+procedure checkCakeWalk(Me, Gear: PGear; var ap: TAttackParams);
+var i: Longword;
+ v: LongInt;
begin
-checkCakeWalk:= BadTurn
+while (not TestColl(hwRound(Gear^.X), hwRound(Gear^.Y), 6)) and (Gear^.Y.Round < LAND_HEIGHT) do
+ Gear^.Y:= Gear^.Y + _1;
+
+for i:= 0 to 2040 do
+ begin
+ cakeStep(Gear);
+ v:= RateExplosion(Me, hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg * 2, afTrackFall);
+ if v > ap.Power then
+ begin
+ ap.ExplX:= hwRound(Gear^.X);
+ ap.ExplY:= hwRound(Gear^.Y);
+ ap.Power:= v
+ end
+ end;
end;
function TestCake(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt;
@@ -993,26 +1093,33 @@
x, y, trackFall: LongInt;
cake: TGear;
begin
- Level:= Level; // avoid compiler hint
+ if (Level > 2) then
+ exit(BadTurn);
ap.ExplR:= 0;
ap.Time:= 0;
- ap.Power:= 1;
+ ap.Power:= BadTurn; // use it as max score value in checkCakeWalk
+ FillChar(cake, sizeof(cake), 0);
cake.Radius:= 7;
+ cake.CollisionMask:= $FF7F;
// check left direction
cake.Angle:= 3;
cake.dX.isNegative:= true;
cake.X:= Me^.X - _3;
cake.Y:= Me^.Y;
- v1:= checkCakeWalk(@cake);
+ checkCakeWalk(Me, @cake, ap);
+ v1:= ap.Power;
// now try opposite direction
cake.Angle:= 1;
cake.dX.isNegative:= false;
cake.X:= Me^.X + _3;
cake.Y:= Me^.Y;
- v2:= checkCakeWalk(@cake);
+ checkCakeWalk(Me, @cake, ap);
+ v2:= ap.Power;
+
+ ap.Power:= 1;
if (v2 > v1) then
begin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uAILandMarks.pas Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,71 @@
+unit uAILandMarks;
+
+interface
+const markWasHere = $01;
+
+procedure addMark(X, Y: LongInt; mark: byte);
+function checkMark(X, Y: LongInt; mark: byte) : boolean;
+procedure clearAllMarks;
+procedure clearMarks(mark: byte);
+
+procedure initModule;
+procedure freeModule;
+
+implementation
+uses uVariables;
+
+const gr = 2;
+
+var marks: array of array of byte;
+ WIDTH, HEIGHT: Longword;
+
+procedure addMark(X, Y: LongInt; mark: byte);
+begin
+ if((X and LAND_WIDTH_MASK) = 0) and ((Y and LAND_HEIGHT_MASK) = 0) then
+ begin
+ X:= X shr gr;
+ Y:= Y shr gr;
+ marks[Y, X]:= marks[Y, X] or mark
+ end
+end;
+
+function checkMark(X, Y: LongInt; mark: byte) : boolean;
+begin
+ checkMark:= ((X and LAND_WIDTH_MASK) = 0)
+ and ((Y and LAND_HEIGHT_MASK) = 0)
+ and ((marks[Y shr gr, X shr gr] and mark) <> 0)
+end;
+
+procedure clearAllMarks;
+var
+ Y, X: Longword;
+begin
+ for Y:= 0 to Pred(HEIGHT) do
+ for X:= 0 to Pred(WIDTH) do
+ marks[Y, X]:= 0
+end;
+
+procedure clearMarks(mark: byte);
+var
+ Y, X: Longword;
+begin
+ for Y:= 0 to Pred(HEIGHT) do
+ for X:= 0 to Pred(WIDTH) do
+ marks[Y, X]:= marks[Y, X] and (not mark)
+end;
+
+
+procedure initModule;
+begin
+ WIDTH:= LAND_WIDTH shr gr;
+ HEIGHT:= LAND_HEIGHT shr gr;
+
+ SetLength(marks, HEIGHT, WIDTH);
+end;
+
+procedure freeModule;
+begin
+ SetLength(marks, 0, 0);
+end;
+
+end.
--- a/hedgewars/uAIMisc.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAIMisc.pas Thu Aug 30 13:02:19 2012 -0400
@@ -86,12 +86,13 @@
ar: array[0..Pred(MAXBONUS div 8)] of TBonus; // don't use too many
end;
+const KillScore = 200;
+var friendlyfactor: LongInt = 300;
+
implementation
uses uCollisions, uVariables, uUtils, uDebug, uLandTexture;
-const KillScore = 200;
-
-var friendlyfactor: LongInt = 300;
+var
KnownExplosion: record
X, Y, Radius: LongInt
end = (X: 0; Y: 0; Radius: 0);
@@ -170,7 +171,7 @@
begin
case Gear^.Kind of
gtCase:
- AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 33, 25);
+ AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y) + 3, 37, 25);
gtFlame:
if (Gear^.State and gsttmpFlag) <> 0 then
AddBonus(hwRound(Gear^.X), hwRound(Gear^.Y), 20, -50);
@@ -371,13 +372,13 @@
x:= x + dX;
y:= y + dY;
dY:= dY + cGravityf;
-(*
- if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then
+
+{ if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then
begin
LandPixels[trunc(y), trunc(x)]:= v;
UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
- end;
-*)
+ end;}
+
// consider adding dX/dY calc here for fall damage
if TestCollExcludingObjects(trunc(x), trunc(y), cHHRadius) then
@@ -439,21 +440,21 @@
end;
if fallDmg < 0 then // drowning. score healthier hogs higher, since their death is more likely to benefit the AI
if Score > 0 then
- inc(rate, KillScore + Score div 10) // Add a bit of a bonus for bigger hog drownings
+ inc(rate, (KillScore + Score div 10) * 1024) // Add a bit of a bonus for bigger hog drownings
else
- dec(rate, KillScore * friendlyfactor div 100 - Score div 10) // and more of a punishment for drowning bigger friendly hogs
+ dec(rate, (KillScore * friendlyfactor div 100 - Score div 10) * 1024) // and more of a punishment for drowning bigger friendly hogs
else if (dmg+fallDmg) >= abs(Score) then
if Score > 0 then
- inc(rate, KillScore)
+ inc(rate, KillScore * 1024 + (dmg + fallDmg)) // tiny bonus for dealing more damage than needed to kill
else
- dec(rate, KillScore * friendlyfactor div 100)
+ dec(rate, KillScore * friendlyfactor div 100 * 1024)
else
if Score > 0 then
- inc(rate, dmg+fallDmg)
- else dec(rate, (dmg+fallDmg) * friendlyfactor div 100)
+ inc(rate, (dmg + fallDmg) * 1024)
+ else dec(rate, (dmg + fallDmg) * friendlyfactor div 100 * 1024)
end;
end;
-RateExplosion:= rate * 1024;
+RateExplosion:= rate;
end;
function RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
@@ -624,6 +625,12 @@
end;
repeat
+ {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
+ begin
+ LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color;
+ UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
+ end;}
+
if not (hwRound(Gear^.Y) + cHHRadius < cWaterLine) then
exit(false);
if (Gear^.State and gstMoving) <> 0 then
@@ -645,7 +652,7 @@
Gear^.Y:= Gear^.Y + Gear^.dY;
if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then
begin
- Gear^.State:= Gear^.State and not (gstMoving or gstHHJumping);
+ Gear^.State:= Gear^.State and (not (gstMoving or gstHHJumping));
Gear^.dY:= _0;
case JumpType of
jmpHJump:
@@ -673,6 +680,7 @@
var pX, pY, tY: LongInt;
begin
HHGo:= false;
+Gear^.CollisionMask:= $FF7F;
AltGear^:= Gear^;
GoInfo.Ticks:= 0;
@@ -680,6 +688,12 @@
GoInfo.JumpType:= jmpNone;
tY:= hwRound(Gear^.Y);
repeat
+ {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
+ begin
+ LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
+ UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
+ end;}
+
pX:= hwRound(Gear^.X);
pY:= hwRound(Gear^.Y);
if pY + cHHRadius >= cWaterLine then
@@ -696,7 +710,7 @@
Gear^.dY:= Gear^.dY + cGravity;
if Gear^.dY > _0_4 then
begin
- Goinfo.FallPix:= 0;
+ GoInfo.FallPix:= 0;
// try ljump instead of fall with damage
HHJump(AltGear, jmpLJump, GoInfo);
if AltGear^.Hedgehog^.BotLevel < 4 then
@@ -709,7 +723,7 @@
if TestCollisionYwithGear(Gear, 1) <> 0 then
begin
inc(GoInfo.Ticks, 410);
- Gear^.State:= Gear^.State and not (gstMoving or gstHHJumping);
+ Gear^.State:= Gear^.State and (not (gstMoving or gstHHJumping));
Gear^.dY:= _0;
// try ljump instead of fall
HHJump(AltGear, jmpLJump, GoInfo);
--- a/hedgewars/uAmmos.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uAmmos.pas Thu Aug 30 13:02:19 2012 -0400
@@ -374,7 +374,7 @@
with CurWeapon^ do
begin
s:= trammo[Ammoz[AmmoType].NameId];
- if (Count <> AMMO_INFINITE) and not (Hedgehog.Team^.ExtDriven or (Hedgehog.BotLevel > 0)) then
+ if (Count <> AMMO_INFINITE) and (not (Hedgehog.Team^.ExtDriven or (Hedgehog.BotLevel > 0))) then
s:= s + ' (' + IntToStr(Count) + ')';
if (Propz and ammoprop_Timerable) <> 0 then
s:= s + ', ' + IntToStr(Timer div 1000) + ' ' + trammo[sidSeconds];
@@ -386,7 +386,7 @@
end
else
begin
- if Gear <> nil then Gear^.State:= Gear^.State and not gstHHChooseTarget;
+ if Gear <> nil then Gear^.State:= Gear^.State and (not gstHHChooseTarget);
isCursorVisible:= false
end;
end
--- a/hedgewars/uCollisions.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uCollisions.pas Thu Aug 30 13:02:19 2012 -0400
@@ -82,7 +82,7 @@
X:= hwRound(Gear^.X);
Y:= hwRound(Gear^.Y);
Radius:= Gear^.Radius;
- ChangeRoundInLand(X, Y, Radius - 1, true, Gear = CurrentHedgehog^.Gear);
+ ChangeRoundInLand(X, Y, Radius - 1, true, (Gear = CurrentHedgehog^.Gear) or (Gear^.Kind = gtCase));
cGear:= Gear
end;
Gear^.CollisionIndex:= Count;
@@ -103,7 +103,7 @@
if Gear^.CollisionIndex >= 0 then
begin
with cinfos[Gear^.CollisionIndex] do
- ChangeRoundInLand(X, Y, Radius - 1, false, Gear = CurrentHedgehog^.Gear);
+ ChangeRoundInLand(X, Y, Radius - 1, false, (Gear = CurrentHedgehog^.Gear) or (Gear^.Kind = gtCase));
cinfos[Gear^.CollisionIndex]:= cinfos[Pred(Count)];
cinfos[Gear^.CollisionIndex].cGear^.CollisionIndex:= Gear^.CollisionIndex;
Gear^.CollisionIndex:= -1;
@@ -138,7 +138,7 @@
var x, y, i: LongInt;
begin
// Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = $FF7F) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = $FF7F) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
((hwRound(Gear^.Hedgehog^.Gear^.X) + Gear^.Hedgehog^.Gear^.Radius + 4 < hwRound(Gear^.X) - Gear^.Radius) or
(hwRound(Gear^.Hedgehog^.Gear^.X) - Gear^.Hedgehog^.Gear^.Radius - 4 > hwRound(Gear^.X) + Gear^.Radius)) then
Gear^.CollisionMask:= $FFFF;
@@ -168,7 +168,7 @@
var x, y, i: LongInt;
begin
// Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = $FF7F) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = $FF7F) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
((hwRound(Gear^.Hedgehog^.Gear^.Y) + Gear^.Hedgehog^.Gear^.Radius + 4 < hwRound(Gear^.Y) - Gear^.Radius) or
(hwRound(Gear^.Hedgehog^.Gear^.Y) - Gear^.Hedgehog^.Gear^.Radius - 4 > hwRound(Gear^.Y) + Gear^.Radius)) then
Gear^.CollisionMask:= $FFFF;
--- a/hedgewars/uCommands.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uCommands.pas Thu Aug 30 13:02:19 2012 -0400
@@ -93,9 +93,12 @@
begin
if t^.Name = CmdStr then
begin
- if t^.Rand then CheckSum:= CheckSum xor LongWord(SDLNet_Read32(@CmdStr)) xor LongWord(s[0]) xor GameTicks;
if TrustedSource or t^.Trusted then
+ begin
+ if t^.Rand and (not CheckNoTeamOrHH) then
+ CheckSum:= CheckSum xor LongWord(SDLNet_Read32(@CmdStr)) xor LongWord(s[0]) xor GameTicks;
t^.Handler(s);
+ end;
exit
end
else
--- a/hedgewars/uConsts.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uConsts.pas Thu Aug 30 13:02:19 2012 -0400
@@ -28,6 +28,7 @@
const
sfMax = 1000;
cDefaultParamNum = 17;
+ cVideorecParamNum = cDefaultParamNum + 7;
// message constants
errmsgCreateSurface = 'Error creating SDL surface';
@@ -146,6 +147,7 @@
cBarrelHealth = 60;
cShotgunRadius = 22;
cBlowTorchC = 6;
+ cakeDmg = 75;
cKeyMaxIndex = 1023;
cKbdMaxIndex = 65536;//need more room for the modifier keys
@@ -244,6 +246,7 @@
gmRemoveFromList = $00004000;
gmAddToList = $00008000;
+ gmDelete = $00010000;
gmAllStoppable = gmLeft or gmRight or gmUp or gmDown or gmAttack or gmPrecise;
cMaxSlotIndex = 9;
@@ -266,6 +269,7 @@
ammoprop_NeedUpDown = $00008000;//Used by TouchInterface to show or hide up/down widgets
ammoprop_OscAim = $00010000;
ammoprop_NoMoveAfter = $00020000;
+ ammoprop_Track = $00040000;
ammoprop_NoRoundEnd = $10000000;
AMMO_INFINITE = 100;
--- a/hedgewars/uFloat.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uFloat.pas Thu Aug 30 13:02:19 2012 -0400
@@ -119,6 +119,7 @@
_0_005: hwFloat = (isNegative: false; QWordValue: 21474836);
_0_008: hwFloat = (isNegative: false; QWordValue: 34359738);
_0_01: hwFloat = (isNegative: false; QWordValue: 42949673);
+ _0_0128: hwFloat = (isNegative: false; QWordValue: 54975581);
_0_02: hwFloat = (isNegative: false; QWordValue: 85899345);
_0_03: hwFloat = (isNegative: false; QWordValue: 128849018);
_0_07: hwFloat = (isNegative: false; QWordValue: 300647710);
@@ -149,14 +150,18 @@
_0: hwFloat = (isNegative: false; QWordValue: 0);
_1: hwFloat = (isNegative: false; QWordValue: 4294967296);
_1_5: hwFloat = (isNegative: false; QWordValue: 4294967296 * 3 div 2);
+ _1_6: hwFloat = (isNegative: false; QWordValue: 4294967296 * 8 div 5);
_1_9: hwFloat = (isNegative: false; QWordValue: 8160437862);
_2: hwFloat = (isNegative: false; QWordValue: 4294967296 * 2);
+ _2_4: hwFloat = (isNegative: false; QWordValue: 4294967296 * 12 div 5);
_3: hwFloat = (isNegative: false; QWordValue: 4294967296 * 3);
_PI: hwFloat = (isNegative: false; QWordValue: 13493037704);
_4: hwFloat = (isNegative: false; QWordValue: 4294967296 * 4);
_4_5: hwFloat = (isNegative: false; QWordValue: 4294967296 * 9 div 2);
_5: hwFloat = (isNegative: false; QWordValue: 4294967296 * 5);
_6: hwFloat = (isNegative: false; QWordValue: 4294967296 * 6);
+ _6_4: hwFloat = (isNegative: false; QWordValue: 3435973837 * 8);
+ _7: hwFloat = (isNegative: false; QWordValue: 4294967296 * 7);
_10: hwFloat = (isNegative: false; QWordValue: 4294967296 * 10);
_12: hwFloat = (isNegative: false; QWordValue: 4294967296 * 12);
_16: hwFloat = (isNegative: false; QWordValue: 4294967296 * 16);
@@ -165,6 +170,8 @@
_25: hwFloat = (isNegative: false; QWordValue: 4294967296 * 25);
_30: hwFloat = (isNegative: false; QWordValue: 4294967296 * 30);
_40: hwFloat = (isNegative: false; QWordValue: 4294967296 * 40);
+ _41: hwFloat = (isNegative: false; QWordValue: 4294967296 * 41);
+ _49: hwFloat = (isNegative: false; QWordValue: 4294967296 * 49);
_50: hwFloat = (isNegative: false; QWordValue: 4294967296 * 50);
_70: hwFloat = (isNegative: false; QWordValue: 4294967296 * 70);
_90: hwFloat = (isNegative: false; QWordValue: 4294967296 * 90);
@@ -196,20 +203,21 @@
{$IFDEF FPC}
-function int2hwFloat (const i: LongInt) : hwFloat;
+function int2hwFloat (const i: LongInt) : hwFloat; inline;
begin
int2hwFloat.isNegative:= i < 0;
int2hwFloat.Round:= abs(i);
int2hwFloat.Frac:= 0
end;
-function hwFloat2Float (const i: hwFloat) : extended;
+function hwFloat2Float (const i: hwFloat) : extended; inline;
begin
-hwFloat2Float:= i.QWordValue / $100000000;
+hwFloat2Float:= i.Frac / $100000000 + i.Round;
if i.isNegative then
hwFloat2Float:= -hwFloat2Float;
end;
+{$IFNDEF WEB}
operator = (const z1, z2: hwFloat) z : boolean; inline;
begin
z:= (z1.isNegative = z2.isNegative) and (z1.QWordValue = z2.QWordValue);
@@ -222,7 +230,7 @@
end;
{$ENDIF}
-operator + (const z1, z2: hwFloat) z : hwFloat;
+operator + (const z1, z2: hwFloat) z : hwFloat; inline;
begin
if z1.isNegative = z2.isNegative then
begin
@@ -242,7 +250,7 @@
end
end;
-operator - (const z1, z2: hwFloat) z : hwFloat;
+operator - (const z1, z2: hwFloat) z : hwFloat; inline;
begin
if z1.isNegative = z2.isNegative then
if z1.QWordValue > z2.QWordValue then
@@ -262,27 +270,150 @@
end
end;
-operator - (const z1: hwFloat) z : hwFloat;
+function isZero(const z: hwFloat): boolean; inline;
+begin
+isZero := z.QWordValue = 0;
+end;
+
+operator < (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+ b:= z1.isNegative
+else
+ if z1.QWordValue = z2.QWordValue then
+ b:= false
+ else
+ b:= not((z1.QWordValue = z2.QWordValue) or ((z2.QWordValue < z1.QWordValue) <> z1.isNegative))
+end;
+
+operator > (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+ b:= z2.isNegative
+else
+ if z1.QWordValue = z2.QWordValue then
+ b:= false
+ else
+ b:= (z1.QWordValue > z2.QWordValue) <> z2.isNegative
+end;
+{$ENDIF}
+{$IFDEF WEB}
+(*
+ Mostly to be kind to JS as of 2012-08-27 where there is no int64/uint64. This may change though.
+*)
+operator = (const z1, z2: hwFloat) z : boolean; inline;
+begin
+ z:= (z1.isNegative = z2.isNegative) and (z1.Frac = z2.Frac) and (z1.Round = z2.Round);
+end;
+
+operator <> (const z1, z2: hwFloat) z : boolean; inline;
+begin
+ z:= (z1.isNegative <> z2.isNegative) or (z1.Frac <> z2.Frac) or (z1.Round <> z2.Round);
+end;
+
+operator + (const z1, z2: hwFloat) z : hwFloat; inline;
+begin
+if z1.isNegative = z2.isNegative then
+ begin
+ z:= z1;
+ z.Frac:= z.Frac + z2.Frac;
+ z.Round:= z.Round + z2.Round;
+ if z.Frac<z1.Frac then inc(z.Round)
+ end
+else
+ if (z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac)) then
+ begin
+ z.isNegative:= z1.isNegative;
+ z.Round:= z1.Round - z2.Round;
+ z.Frac:= z1.Frac - z2.Frac;
+ if z2.Frac > z1.Frac then dec(z.Round)
+ end
+ else
+ begin
+ z.isNegative:= z2.isNegative;
+ z.Round:= z2.Round - z1.Round;
+ z.Frac:= z2.Frac-z1.Frac;
+ if z2.Frac < z1.Frac then dec(z.Round)
+ end
+end;
+
+operator - (const z1, z2: hwFloat) z : hwFloat; inline;
+begin
+if z1.isNegative = z2.isNegative then
+ if (z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac)) then
+ begin
+ z.isNegative:= z1.isNegative;
+ z.Round:= z1.Round - z2.Round;
+ z.Frac:= z1.Frac-z2.Frac;
+ if z2.Frac > z1.Frac then dec(z.Round)
+ end
+ else
+ begin
+ z.isNegative:= not z2.isNegative;
+ z.Round:= z2.Round - z1.Round;
+ z.Frac:= z2.Frac-z1.Frac;
+ if z2.Frac < z1.Frac then dec(z.Round)
+ end
+else
+ begin
+ z:= z1;
+ z.Frac:= z.Frac + z2.Frac;
+ z.Round:= z.Round + z2.Round;
+ if z.Frac<z1.Frac then inc(z.Round)
+ end
+end;
+
+operator < (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+ b:= z1.isNegative
+else
+(* Not so sure this specialcase is a win w/ Round/Frac. have to do more tests anyway.
+ if (z1.Round = z2.Round and (z1.Frac = z2.Frac)) then
+ b:= false
+ else *)
+ b:= ((z1.Round < z2.Round) or ((z1.Round = z2.Round) and (z1.Frac < z2.Frac))) <> z1.isNegative
+end;
+
+operator > (const z1, z2: hwFloat) b : boolean; inline;
+begin
+if z1.isNegative xor z2.isNegative then
+ b:= z2.isNegative
+else
+(*
+ if z1.QWordValue = z2.QWordValue then
+ b:= false
+ else*)
+ b:= ((z1.Round > z2.Round) or ((z1.Round = z2.Round) and (z1.Frac > z2.Frac))) <> z1.isNegative
+end;
+
+function isZero(const z: hwFloat): boolean; inline;
+begin
+isZero := z.Round = 0 and z.Frac = 0;
+end;
+{$ENDIF}
+
+operator - (const z1: hwFloat) z : hwFloat; inline;
begin
z:= z1;
z.isNegative:= not z.isNegative
end;
-operator * (const z1, z2: hwFloat) z : hwFloat;
+operator * (const z1, z2: hwFloat) z : hwFloat; inline;
begin
z.isNegative:= z1.isNegative xor z2.isNegative;
z.QWordValue:= QWord(z1.Round) * z2.Frac + QWord(z1.Frac) * z2.Round + ((QWord(z1.Frac) * z2.Frac) shr 32);
z.Round:= z.Round + QWord(z1.Round) * z2.Round;
end;
-operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat;
+operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
begin
z.isNegative:= z1.isNegative xor (z2 < 0);
z.QWordValue:= z1.QWordValue * abs(z2)
end;
-operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat;
+operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline;
var t: hwFloat;
begin
z.isNegative:= z1.isNegative xor z2.isNegative;
@@ -304,34 +435,12 @@
end
end;
-operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat;
+operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
begin
z.isNegative:= z1.isNegative xor (z2 < 0);
z.QWordValue:= z1.QWordValue div abs(z2)
end;
-operator < (const z1, z2: hwFloat) b : boolean;
-begin
-if z1.isNegative xor z2.isNegative then
- b:= z1.isNegative
-else
- if z1.QWordValue = z2.QWordValue then
- b:= false
- else
- b:= (z1.QWordValue < z2.QWordValue) xor z1.isNegative
-end;
-
-operator > (const z1, z2: hwFloat) b : boolean;
-begin
-if z1.isNegative xor z2.isNegative then
- b:= z2.isNegative
-else
- if z1.QWordValue = z2.QWordValue then
- b:= false
- else
- b:= (z1.QWordValue > z2.QWordValue) xor z2.isNegative
-end;
-
function cstr(const z: hwFloat): shortstring;
var tmpstr: shortstring;
begin
@@ -360,7 +469,7 @@
hwAbs.isNegative:= false
end;
-function hwSqr(const t: hwFloat): hwFloat;
+function hwSqr(const t: hwFloat): hwFloat; inline;
begin
hwSqr.isNegative:= false;
hwSqr.QWordValue:= ((QWord(t.Round) * t.Round) shl 32) + QWord(t.Round) * t.Frac * 2 + ((QWord(t.Frac) * t.Frac) shr 32);
@@ -369,7 +478,7 @@
function hwPow(const t: hwFloat;p: LongWord): hwFloat;
begin
hwPow:= t;
-if p mod 2 = 0 then hwPow.isNegative:= t.isNegative;
+if p mod 2 = 0 then hwPow.isNegative:= false;
while p > 0 do
begin
@@ -461,11 +570,6 @@
else
AngleCos.QWordValue:= SinTable[Angle - 1024]
end;
-
-function isZero(const z: hwFloat): boolean; inline;
-begin
-isZero := z.QWordValue = 0;
-end;
{$ENDIF}
end.
--- a/hedgewars/uGame.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGame.pas Thu Aug 30 13:02:19 2012 -0400
@@ -39,17 +39,27 @@
isInLag:= false;
SendKeepAliveMessage(Lag)
end;
-if Lag > 100 then
- Lag:= 100
-else if (GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) then
- Lag:= 2500;
+if GameType <> gmtRecord then
+ begin
+ if Lag > 100 then
+ Lag:= 100
+ else if (GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) then
+ Lag:= 2500;
-if (GameType = gmtDemo) then
- if isSpeed then
- Lag:= Lag * 10
- else
- if cOnlyStats then
- Lag:= High(LongInt);
+ if (GameType = gmtDemo) then
+ if isSpeed then
+ begin
+ i:= RealTicks-SpeedStart;
+ if i < 2000 then Lag:= Lag*5
+ else if i < 4000 then Lag:= Lag*10
+ else if i < 6000 then Lag:= Lag*20
+ else if i < 8000 then Lag:= Lag*40
+ else Lag:= Lag*80;
+ end
+ else
+ if cOnlyStats then
+ Lag:= High(LongInt);
+ end;
PlayNextVoice;
i:= 1;
while (GameState <> gsExit) and (i <= Lag) do
@@ -71,7 +81,7 @@
AddVisualGear(0, 0, vgtTeamHealthSorter);
break;
end;
- gmtDemo: begin
+ gmtDemo, gmtRecord: begin
GameState:= gsExit;
exit
end;
--- a/hedgewars/uGears.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGears.pas Thu Aug 30 13:02:19 2012 -0400
@@ -59,7 +59,7 @@
uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics,
uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uMobile, uVariables,
uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture,
- uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlers;
+ uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlers, uGearsHandlersRope;
var skipFlag: boolean;
@@ -77,6 +77,7 @@
stAfterDelay, stChWin, stWater, stChWin2, stHealth,
stSpawn, stNTurn);
upd: Longword;
+ snowLeft,snowRight: LongInt;
//SDMusic: shortstring;
// For better maintainability the step handlers of gears are stored in
@@ -206,23 +207,28 @@
curHandledGear:= t;
t:= curHandledGear^.NextGear;
- if curHandledGear^.Message and gmRemoveFromList <> 0 then
- begin
- RemoveGearFromList(curHandledGear);
- // since I can't think of any good reason this would ever be separate from a remove from list, going to keep it inside this block
- if curHandledGear^.Message and gmAddToList <> 0 then InsertGearToList(curHandledGear);
- curHandledGear^.Message:= curHandledGear^.Message and not (gmRemoveFromList or gmAddToList)
- end;
- if curHandledGear^.Active then
+ if curHandledGear^.Message and gmDelete <> 0 then
+ DeleteGear(curHandledGear)
+ else
begin
- if curHandledGear^.RenderTimer and (curHandledGear^.Timer > 500) and ((curHandledGear^.Timer mod 1000) = 0) then
+ if curHandledGear^.Message and gmRemoveFromList <> 0 then
+ begin
+ RemoveGearFromList(curHandledGear);
+ // since I can't think of any good reason this would ever be separate from a remove from list, going to keep it inside this block
+ if curHandledGear^.Message and gmAddToList <> 0 then InsertGearToList(curHandledGear);
+ curHandledGear^.Message:= curHandledGear^.Message and (not (gmRemoveFromList or gmAddToList))
+ end;
+ if curHandledGear^.Active then
begin
- FreeTexture(curHandledGear^.Tex);
- curHandledGear^.Tex:= RenderStringTex(inttostr(curHandledGear^.Timer div 1000), cWhiteColor, fntSmall);
- end;
- curHandledGear^.doStep(curHandledGear);
- // might be useful later
- //ScriptCall('onGearStep', Gear^.uid);
+ if curHandledGear^.RenderTimer and (curHandledGear^.Timer > 500) and ((curHandledGear^.Timer mod 1000) = 0) then
+ begin
+ FreeTexture(curHandledGear^.Tex);
+ curHandledGear^.Tex:= RenderStringTex(inttostr(curHandledGear^.Timer div 1000), cWhiteColor, fntSmall);
+ end;
+ curHandledGear^.doStep(curHandledGear);
+ // might be useful later
+ //ScriptCall('onGearStep', Gear^.uid);
+ end
end
end;
curHandledGear:= nil;
@@ -634,18 +640,21 @@
if (GameFlags and gfArtillery) <> 0 then
cArtillery:= true;
-
-for i:= 0 to GetRandom(10)+30 do
- begin rx:= GetRandom(rightX-leftX)+leftX;
+for i:= GetRandom(10)+30 downto 0 do
+ begin
+ rx:= GetRandom(rightX-leftX)+leftX;
ry:= GetRandom(LAND_HEIGHT-topY)+topY;
rdx:= _90-(GetRandomf*_360);
rdy:= _90-(GetRandomf*_360);
AddGear(rx, ry, gtGenericFaller, gstInvisible, rdx, rdy, $FFFFFFFF);
end;
+snowRight:= max(LAND_WIDTH,4096)+512;
+snowLeft:= -(snowRight-LAND_WIDTH);
+
if not hasBorder and ((Theme = 'Snow') or (Theme = 'Christmas')) then
- for i:= 0 to Pred(vobCount*2) do
- AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(LAND_HEIGHT div 2), gtFlake, 0, _0, _0, 0);
+ for i:= vobCount * max(LAND_WIDTH,4096) div 2048 downto 1 do
+ AddGear(GetRandom(snowRight-snowLeft)+snowLeft, LAND_HEIGHT-1300+GetRandom(750), gtFlake, 0, _0, _0, 0);
end;
--- a/hedgewars/uGearsHandlers.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsHandlers.pas Thu Aug 30 13:02:19 2012 -0400
@@ -52,8 +52,8 @@
dA := hwSign(Gear^.dX);
xx := dirs[Gear^.Angle].x;
yy := dirs[Gear^.Angle].y;
- xxn := dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].x;
- yyn := dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].y;
+ xxn := dirs[(Gear^.Angle + dA) and 3].x;
+ yyn := dirs[(Gear^.Angle + dA) and 3].y;
if (xx = 0) then
if TestCollisionYwithGear(Gear, yy) <> 0 then
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uGearsHandlersRope.pas Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,589 @@
+(*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *)
+
+{$INCLUDE "options.inc"}
+unit uGearsHandlersRope;
+interface
+
+uses uTypes;
+
+procedure doStepRope(Gear: PGear);
+
+implementation
+uses uConsts, uFloat, uCollisions, uVariables, uGearsList, uSound, uGearsUtils,
+ uAmmos, uDebug, uUtils, uGearsHedgehog, uGearsRender;
+
+procedure doStepRopeAfterAttack(Gear: PGear);
+var
+ HHGear: PGear;
+begin
+ HHGear := Gear^.Hedgehog^.Gear;
+ if ((HHGear^.State and gstHHDriven) = 0)
+ or (CheckGearDrowning(HHGear))
+ or (TestCollisionYwithGear(HHGear, 1) <> 0) then
+ begin
+ DeleteGear(Gear);
+ isCursorVisible := false;
+ ApplyAmmoChanges(HHGear^.Hedgehog^);
+ exit
+ end;
+
+ HedgehogChAngle(HHGear);
+
+ if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+ SetLittle(HHGear^.dX);
+
+ if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
+ HHGear^.dY := _0;
+ HHGear^.X := HHGear^.X + HHGear^.dX;
+ HHGear^.Y := HHGear^.Y + HHGear^.dY;
+ HHGear^.dY := HHGear^.dY + cGravity;
+
+ if (GameFlags and gfMoreWind) <> 0 then
+ HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density;
+
+ if (Gear^.Message and gmAttack) <> 0 then
+ begin
+ Gear^.X := HHGear^.X;
+ Gear^.Y := HHGear^.Y;
+
+ ApplyAngleBounds(Gear^.Hedgehog^, amRope);
+
+ Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX);
+ Gear^.dY := -AngleCos(HHGear^.Angle);
+ Gear^.Friction := _4_5 * cRopePercent;
+ Gear^.Elasticity := _0;
+ Gear^.State := Gear^.State and (not gsttmpflag);
+ Gear^.doStep := @doStepRope;
+ end
+end;
+
+procedure unstickHog(Gear, HHGear: PGear);
+var i: LongInt;
+ stuck: Boolean;
+begin
+ if (TestCollisionYwithGear(HHGear, 1) <> 0) and (TestCollisionYwithGear(HHGear, -1) = 0) then
+ begin
+ i:= 1;
+ repeat
+ begin
+ inc(i);
+ stuck:= TestCollisionYwithGear(HHGear, 1) <> 0;
+ if stuck then HHGear^.Y:= HHGear^.Y-_1
+ end
+ until (i = 8) or (not stuck);
+ HHGear^.Y:= HHGear^.Y+_1;
+ // experiment in simulating something the shoppa players apparently expect
+ if Gear^.Message and gmDown <> 0 then
+ begin
+ //HHGear^.dY:= HHGear^.dY / 16;
+ //HHGear^.dY.QWordValue:= 0;
+ HHGear^.dY:= -_0_1;
+ HHGear^.dX:= HHGear^.dX * _1_5;
+ end;
+ if Gear^.Message and gmRight <> 0 then
+ HHGear^.dX.isNegative:= false
+ else if Gear^.Message and gmLeft <> 0 then
+ HHGear^.dX.isNegative:= true
+ end
+ else if (TestCollisionYwithGear(HHGear, -1) <> 0) and (TestCollisionYwithGear(HHGear, 1) = 0) then
+ begin
+ i:= 1;
+ repeat
+ begin
+ inc(i);
+ stuck:= TestCollisionYwithGear(HHGear, -1) <> 0;
+ if stuck then HHGear^.Y:= HHGear^.Y+_1
+ end
+ until (i = 8) or (not stuck);
+ HHGear^.Y:= HHGear^.Y-_1;
+ if Gear^.Message and gmDown <> 0 then
+ begin
+ //HHGear^.dY:= HHGear^.dY / 16;
+ //HHGear^.dY.QWordValue:= 0;
+ HHGear^.dY:= _0_1;
+ HHGear^.dX:= HHGear^.dX * _1_5;
+ end;
+ if Gear^.Message and gmRight <> 0 then
+ HHGear^.dX.isNegative:= true
+ else if Gear^.Message and gmLeft <> 0 then
+ HHGear^.dX.isNegative:= false
+ end;
+ if TestCollisionXwithGear(HHGear, 1) and (not TestCollisionXwithGear(HHGear, -1)) then
+ begin
+ i:= 1;
+ repeat
+ begin
+ inc(i);
+ stuck:= TestCollisionXwithGear(HHGear, 1);
+ if stuck then HHGear^.X:= HHGear^.X-_1
+ end
+ until (i = 8) or (not stuck);
+ HHGear^.X:= HHGear^.X+_1;
+ if Gear^.Message and gmDown <> 0 then
+ begin
+ //HHGear^.dX:= HHGear^.dX / 16;
+ //HHGear^.dX.QWordValue:= 0;
+ HHGear^.dX:= -_0_1;
+ HHGear^.dY:= HHGear^.dY * _1_5;
+ end;
+ if Gear^.Message and gmRight <> 0 then
+ HHGear^.dY.isNegative:= true
+ else if Gear^.Message and gmLeft <> 0 then
+ HHGear^.dY.isNegative:= false
+ end
+ else if TestCollisionXwithGear(HHGear, -1) and (not TestCollisionXwithGear(HHGear, 1)) then
+ begin
+ i:= 1;
+ repeat
+ begin
+ inc(i);
+ stuck:= TestCollisionXwithGear(HHGear, -1);
+ if stuck then HHGear^.X:= HHGear^.X+_1
+ end
+ until (i = 8) or (not stuck);
+ HHGear^.X:= HHGear^.X-_1;
+ if Gear^.Message and gmDown <> 0 then
+ begin
+ //HHGear^.dX:= HHGear^.dX / 16;
+ //HHGear^.dX.QWordValue:= 0;
+ HHGear^.dX:= _0_1;
+ HHGear^.dY:= HHGear^.dY * _1_5;
+ end;
+ if Gear^.Message and gmRight <> 0 then
+ HHGear^.dY.isNegative:= false
+ else if Gear^.Message and gmLeft <> 0 then
+ HHGear^.dY.isNegative:= true
+ end
+end;
+
+procedure RopeDeleteMe(Gear, HHGear: PGear);
+begin
+ PlaySound(sndRopeRelease);
+ HHGear^.dX.QWordValue:= HHGear^.dX.QWordValue div Gear^.stepFreq;
+ HHGear^.dY.QWordValue:= HHGear^.dY.QWordValue div Gear^.stepFreq;
+ with HHGear^ do
+ begin
+ Message := Message and (not gmAttack);
+ State := (State or gstMoving) and (not gstWinner);
+ end;
+ unstickHog(Gear, HHGear);
+ DeleteGear(Gear)
+end;
+
+procedure RopeWaitCollision(Gear, HHGear: PGear);
+begin
+ PlaySound(sndRopeRelease);
+ with HHGear^ do
+ begin
+ Message := Message and (not gmAttack);
+ State := State or gstMoving;
+ end;
+ unstickHog(Gear, HHGear);
+ RopePoints.Count := 0;
+ Gear^.Elasticity := _0;
+ Gear^.doStep := @doStepRopeAfterAttack;
+ HHGear^.dX.QWordValue:= HHGear^.dX.QWordValue div Gear^.stepFreq;
+ HHGear^.dY.QWordValue:= HHGear^.dY.QWordValue div Gear^.stepFreq;
+ Gear^.stepFreq := 1
+end;
+
+procedure doStepRopeWork(Gear: PGear);
+var
+ HHGear: PGear;
+ len, tx, ty, nx, ny, ropeDx, ropeDy, mdX, mdY, t: hwFloat;
+ lx, ly, cd, i: LongInt;
+ haveCollision,
+ haveDivided: boolean;
+
+begin
+ if GameTicks mod 8 <> 0 then exit;
+
+ HHGear := Gear^.Hedgehog^.Gear;
+ haveCollision:= false;
+ if (Gear^.Message and gmLeft <> 0) and (not TestCollisionXwithGear(HHGear, -1)) then
+ HHGear^.dX := HHGear^.dX - _0_0128
+ else haveCollision:= true;
+
+ if (Gear^.Message and gmRight <> 0) and (not TestCollisionXwithGear(HHGear, 1)) then
+ HHGear^.dX := HHGear^.dX + _0_0128
+ else haveCollision:= true;
+
+
+ if ((HHGear^.State and gstHHDriven) = 0)
+ or (CheckGearDrowning(HHGear)) or (Gear^.PortalCounter <> 0) then
+ begin
+ RopeDeleteMe(Gear, HHGear);
+ exit
+ end;
+
+ // vector between hedgehog and rope attaching point
+ ropeDx := HHGear^.X - Gear^.X;
+ ropeDy := HHGear^.Y - Gear^.Y;
+
+ if TestCollisionYwithGear(HHGear, 1) = 0 then
+ begin
+
+ // depending on the rope vector we know which X-side to check for collision
+ // in order to find out if the hog can still be moved by gravity
+ if ropeDx.isNegative = RopeDy.IsNegative then
+ cd:= -1
+ else
+ cd:= 1;
+
+ // apply gravity if there is no obstacle
+ if not TestCollisionXwithGear(HHGear, cd) then
+ HHGear^.dY := HHGear^.dY + cGravity * 64;
+
+ if (GameFlags and gfMoreWind) <> 0 then
+ // apply wind if there's no obstacle
+ if not TestCollisionXwithGear(HHGear, hwSign(cWindSpeed)) then
+ HHGear^.dX := HHGear^.dX + cWindSpeed * 64 / HHGear^.Density;
+ end
+ else haveCollision:= true;
+
+ if ((Gear^.Message and gmDown) <> 0) and (Gear^.Elasticity < Gear^.Friction) then
+ if not (TestCollisionXwithGear(HHGear, hwSign(ropeDx))
+ or (TestCollisionYwithGear(HHGear, hwSign(ropeDy)) <> 0)) then
+ Gear^.Elasticity := Gear^.Elasticity + _2_4
+ else haveCollision:= true;
+
+ if ((Gear^.Message and gmUp) <> 0) and (Gear^.Elasticity > _30) then
+ if not (TestCollisionXwithGear(HHGear, -hwSign(ropeDx))
+ or (TestCollisionYwithGear(HHGear, -hwSign(ropeDy)) <> 0)) then
+ Gear^.Elasticity := Gear^.Elasticity - _2_4
+ else haveCollision:= true;
+
+(*
+I am not so sure this is useful. Disabling
+ if haveCollision then
+ begin
+ if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) and not TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+ HHGear^.dX.isNegative:= not HHGear^.dX.isNegative;
+ if (TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0) and (TestCollisionYwithGear(HHGear, -hwSign(HHGear^.dY)) = 0) then
+ HHGear^.dY.isNegative:= not HHGear^.dY.isNegative;
+ end;
+*)
+
+ mdX := ropeDx + HHGear^.dX;
+ mdY := ropeDy + HHGear^.dY;
+ len := _1 / Distance(mdX, mdY);
+ // rope vector plus hedgehog direction vector normalized
+ mdX := mdX * len;
+ mdY := mdY * len;
+
+ // for visual purposes only
+ Gear^.dX := mdX;
+ Gear^.dY := mdY;
+
+ /////
+ tx := HHGear^.X;
+ ty := HHGear^.Y;
+
+ HHGear^.X := Gear^.X + mdX * Gear^.Elasticity;
+ HHGear^.Y := Gear^.Y + mdY * Gear^.Elasticity;
+
+ HHGear^.dX := HHGear^.X - tx;
+ HHGear^.dY := HHGear^.Y - ty;
+ ////
+
+
+ haveDivided := false;
+ // check whether rope needs dividing
+
+ len := Gear^.Elasticity - _5;
+ nx := Gear^.X + mdX * len;
+ ny := Gear^.Y + mdY * len;
+ tx := mdX * _2_4; // should be the same as increase step
+ ty := mdY * _2_4;
+
+ while len > _3 do
+ begin
+ lx := hwRound(nx);
+ ly := hwRound(ny);
+ if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and ((Land[ly, lx] and $FF00) <> 0) then
+ begin
+ ny := _1 / Distance(ropeDx, ropeDy);
+ // old rope pos
+ nx := ropeDx * ny;
+ ny := ropeDy * ny;
+
+ with RopePoints.ar[RopePoints.Count] do
+ begin
+ X := Gear^.X;
+ Y := Gear^.Y;
+ if RopePoints.Count = 0 then
+ RopePoints.HookAngle := DxDy2Angle(Gear^.dY, Gear^.dX);
+ b := (nx * HHGear^.dY) > (ny * HHGear^.dX);
+ dLen := len
+ end;
+
+ with RopePoints.rounded[RopePoints.Count] do
+ begin
+ X := hwRound(Gear^.X);
+ Y := hwRound(Gear^.Y);
+ end;
+
+ Gear^.X := Gear^.X + nx * len;
+ Gear^.Y := Gear^.Y + ny * len;
+ inc(RopePoints.Count);
+ TryDo(RopePoints.Count <= MAXROPEPOINTS, 'Rope points overflow', true);
+ Gear^.Elasticity := Gear^.Elasticity - len;
+ Gear^.Friction := Gear^.Friction - len;
+ haveDivided := true;
+ break
+ end;
+ nx := nx - tx;
+ ny := ny - ty;
+
+ // len := len - _2_4 // should be the same as increase step
+ len.QWordValue := len.QWordValue - _2_4.QWordValue;
+ end;
+
+ if not haveDivided then
+ if RopePoints.Count > 0 then // check whether the last dividing point could be removed
+ begin
+ tx := RopePoints.ar[Pred(RopePoints.Count)].X;
+ ty := RopePoints.ar[Pred(RopePoints.Count)].Y;
+ mdX := tx - Gear^.X;
+ mdY := ty - Gear^.Y;
+ if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * (ty - HHGear^.Y) > (tx - HHGear^.X) * mdY) then
+ begin
+ dec(RopePoints.Count);
+ Gear^.X := RopePoints.ar[RopePoints.Count].X;
+ Gear^.Y := RopePoints.ar[RopePoints.Count].Y;
+ Gear^.Elasticity := Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen;
+ Gear^.Friction := Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen;
+
+ // restore hog position
+ len := _1 / Distance(mdX, mdY);
+ mdX := mdX * len;
+ mdY := mdY * len;
+
+ HHGear^.X := Gear^.X - mdX * Gear^.Elasticity;
+ HHGear^.Y := Gear^.Y - mdY * Gear^.Elasticity;
+ end
+ end;
+
+ haveCollision := false;
+ if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+ begin
+ HHGear^.dX := -_0_6 * HHGear^.dX;
+ haveCollision := true
+ end;
+ if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0 then
+ begin
+ HHGear^.dY := -_0_6 * HHGear^.dY;
+ haveCollision := true
+ end;
+
+ if haveCollision and (Gear^.Message and (gmLeft or gmRight) <> 0) and (Gear^.Message and (gmUp or gmDown) <> 0) then
+ begin
+ HHGear^.dX := SignAs(hwAbs(HHGear^.dX) + _1_6, HHGear^.dX);
+ HHGear^.dY := SignAs(hwAbs(HHGear^.dY) + _1_6, HHGear^.dY)
+ end;
+
+ len := hwSqr(HHGear^.dX) + hwSqr(HHGear^.dY);
+ if len > _49 then
+ begin
+ len := _7 / hwSqrt(len);
+ HHGear^.dX := HHGear^.dX * len;
+ HHGear^.dY := HHGear^.dY * len;
+ end;
+
+ haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)]) <> 0);
+
+ if not haveCollision then
+ begin
+ // backup gear location
+ tx:= Gear^.X;
+ ty:= Gear^.Y;
+
+ if RopePoints.Count > 0 then
+ begin
+ // set gear location to the remote end of the rope, the attachment point
+ Gear^.X:= RopePoints.ar[0].X;
+ Gear^.Y:= RopePoints.ar[0].Y;
+ end;
+
+ CheckCollision(Gear);
+ // if we haven't found any collision yet then check the other side too
+ if (Gear^.State and gstCollision) = 0 then
+ begin
+ Gear^.dX.isNegative:= not Gear^.dX.isNegative;
+ Gear^.dY.isNegative:= not Gear^.dY.isNegative;
+ CheckCollision(Gear);
+ Gear^.dX.isNegative:= not Gear^.dX.isNegative;
+ Gear^.dY.isNegative:= not Gear^.dY.isNegative;
+ end;
+
+ haveCollision:= (Gear^.State and gstCollision) <> 0;
+
+ // restore gear location
+ Gear^.X:= tx;
+ Gear^.Y:= ty;
+ end;
+
+ // if the attack key is pressed, lose rope contact as well
+ if (Gear^.Message and gmAttack) <> 0 then
+ haveCollision:= false;
+
+ if not haveCollision then
+ begin
+ if (Gear^.State and gsttmpFlag) <> 0 then
+ begin
+ if Gear^.Hedgehog^.CurAmmoType <> amParachute then
+ RopeWaitCollision(Gear, HHGear)
+ else
+ RopeDeleteMe(Gear, HHGear)
+ end
+ end
+ else
+ if (Gear^.State and gsttmpFlag) = 0 then
+ Gear^.State := Gear^.State or gsttmpFlag;
+end;
+
+procedure RopeRemoveFromAmmo(Gear, HHGear: PGear);
+begin
+ if (Gear^.State and gstAttacked) = 0 then
+ begin
+ OnUsedAmmo(HHGear^.Hedgehog^);
+ Gear^.State := Gear^.State or gstAttacked
+ end;
+ ApplyAmmoChanges(HHGear^.Hedgehog^)
+end;
+
+procedure doStepRopeAttach(Gear: PGear);
+var
+ HHGear: PGear;
+ tx, ty, tt: hwFloat;
+begin
+ Gear^.X := Gear^.X - Gear^.dX;
+ Gear^.Y := Gear^.Y - Gear^.dY;
+ Gear^.Elasticity := Gear^.Elasticity + _1;
+
+ HHGear := Gear^.Hedgehog^.Gear;
+ DeleteCI(HHGear);
+
+ if (HHGear^.State and gstMoving) <> 0 then
+ begin
+ if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
+ SetLittle(HHGear^.dX);
+ if HHGear^.dY.isNegative and (TestCollisionYwithGear(HHGear, -1) <> 0) then
+ HHGear^.dY := _0;
+
+ HHGear^.X := HHGear^.X + HHGear^.dX;
+ Gear^.X := Gear^.X + HHGear^.dX;
+
+ if TestCollisionYwithGear(HHGear, 1) <> 0 then
+ begin
+ CheckHHDamage(HHGear);
+ HHGear^.dY := _0
+ //HHGear^.State:= HHGear^.State and (not (gstHHJumping or gstHHHJump));
+ end
+ else
+ begin
+ HHGear^.Y := HHGear^.Y + HHGear^.dY;
+ Gear^.Y := Gear^.Y + HHGear^.dY;
+ HHGear^.dY := HHGear^.dY + cGravity;
+ if (GameFlags and gfMoreWind) <> 0 then
+ HHGear^.dX := HHGear^.dX + cWindSpeed / HHGear^.Density
+ end;
+
+ tt := Gear^.Elasticity;
+ tx := _0;
+ ty := _0;
+ while tt > _20 do
+ begin
+ if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] and $FF00) <> 0) then
+ begin
+ Gear^.X := Gear^.X + tx;
+ Gear^.Y := Gear^.Y + ty;
+ Gear^.Elasticity := tt;
+ Gear^.doStep := @doStepRopeWork;
+ Gear^.stepFreq:= 8;
+ PlaySound(sndRopeAttach);
+ with HHGear^ do
+ begin
+ dX.QWordValue:= dX.QWordValue shl 3;
+ dY.QWordValue:= dY.QWordValue shl 3;
+ State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
+ Message := Message and (not gmAttack)
+ end;
+
+ RopeRemoveFromAmmo(Gear, HHGear);
+
+ tt := _0;
+ exit
+ end;
+ tx := tx + Gear^.dX + Gear^.dX;
+ ty := ty + Gear^.dY + Gear^.dY;
+ tt := tt - _2;
+ end;
+ end;
+
+ if Gear^.Elasticity < _20 then Gear^.CollisionMask:= $FF00
+ else Gear^.CollisionMask:= $FF7F;
+ CheckCollision(Gear);
+
+ if (Gear^.State and gstCollision) <> 0 then
+ if Gear^.Elasticity < _10 then
+ Gear^.Elasticity := _10000
+ else
+ begin
+ Gear^.doStep := @doStepRopeWork;
+ Gear^.stepFreq:= 8;
+ PlaySound(sndRopeAttach);
+ with HHGear^ do
+ begin
+ dX.QWordValue:= dX.QWordValue shl 3;
+ dY.QWordValue:= dY.QWordValue shl 3;
+ State := State and (not (gstAttacking or gstHHJumping or gstHHHJump));
+ Message := Message and (not gmAttack)
+ end;
+
+ RopeRemoveFromAmmo(Gear, HHGear);
+
+ exit
+ end;
+
+ if (Gear^.Elasticity > Gear^.Friction)
+ or ((Gear^.Message and gmAttack) = 0)
+ or ((HHGear^.State and gstHHDriven) = 0)
+ or (HHGear^.Damage > 0) then
+ begin
+ with Gear^.Hedgehog^.Gear^ do
+ begin
+ State := State and (not gstAttacking);
+ Message := Message and (not gmAttack)
+ end;
+ DeleteGear(Gear);
+ exit;
+ end;
+ if CheckGearDrowning(HHGear) then DeleteGear(Gear)
+end;
+
+procedure doStepRope(Gear: PGear);
+begin
+ Gear^.dX := - Gear^.dX;
+ Gear^.dY := - Gear^.dY;
+ Gear^.doStep := @doStepRopeAttach;
+ PlaySound(sndRopeShot)
+end;
+
+end.
--- a/hedgewars/uGearsHedgehog.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsHedgehog.pas Thu Aug 30 13:02:19 2012 -0400
@@ -234,9 +234,9 @@
and ((Gear^.Message and gmLJump) <> 0)
and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
begin
- newDx:= dX / _2;
- newDy:= dY / _2;
- altUse:= true;
+ newDx:= dX / CurAmmoGear^.stepFreq;
+ newDy:= dY / CurAmmoGear^.stepFreq;
+ altUse:= true
end
else
begin
@@ -260,10 +260,7 @@
amPickHammer: newGear:= AddGear(hwRound(lx), hwRound(ly) + cHHRadius, gtPickHammer, 0, _0, _0, 0);
amSkip: ParseCommand('/skip', true);
amRope: newGear:= AddGear(hwRound(lx), hwRound(ly), gtRope, 0, xx, yy, 0);
- amMine: if altUse then
- newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, newDx, newDy, 3000)
- else
- newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
+ amMine: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
amSMine: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSMine, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
amDEagle: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0);
amSineGun: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0);
@@ -360,6 +357,11 @@
amTardis: newGear:= AddGear(hwRound(X), hwRound(Y), gtTardis, 0, _0, _0, 5000);
amIceGun: newGear:= AddGear(hwRound(X), hwRound(Y), gtIceGun, 0, _0, _0, 0);
end;
+ if altUse then
+ begin
+ newGear^.dX:= newDx / newGear^.Density;
+ newGear^.dY:= newDY / newGear^.Density
+ end;
case CurAmmoType of
amGrenade, amMolotov,
@@ -455,11 +457,13 @@
procedure AfterAttack;
var s: shortstring;
a: TAmmoType;
+ HHGear: PGear;
begin
-with CurrentHedgehog^.Gear^, CurrentHedgehog^ do
+with CurrentHedgehog^ do
begin
+ HHGear:= Gear;
a:= CurAmmoType;
- State:= State and (not gstAttacking);
+ if HHGear <> nil then HHGear^.State:= HHGear^.State and (not gstAttacking);
if (Ammoz[a].Ammo.Propz and ammoprop_Effect) = 0 then
begin
Inc(MultiShootAttacks);
@@ -484,8 +488,8 @@
TagTurnTimeLeft:= TurnTimeLeft;
TurnTimeLeft:=(Ammoz[a].TimeAfterTurn * cGetAwayTime) div 100;
end;
- if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) then
- State:= State or gstAttacked;
+ if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) and (HHGear <> nil) then
+ HHGear^.State:= HHGear^.State or gstAttacked;
if (Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) <> 0 then
ApplyAmmoChanges(CurrentHedgehog^)
end;
@@ -570,7 +574,6 @@
var s: shortstring;
vga: PVisualGear;
begin
- PlaySound(sndShotgunReload);
if cnt <> 0 then AddAmmo(HH, ammo, cnt)
else AddAmmo(HH, ammo);
@@ -612,6 +615,7 @@
case Gear^.Pos of
posCaseUtility,
posCaseAmmo: begin
+ PlaySound(sndShotgunReload);
if Gear^.AmmoType <> amNothing then
begin
AddPickup(HH^.Hedgehog^, Gear^.AmmoType, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y));
@@ -634,7 +638,7 @@
end;
gi := gi^.NextGear
end;
- ag:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAddAmmo, gstInvisible, _0, _0, GetRandom(200)+100);
+ ag:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAddAmmo, gstInvisible, _0, _0, GetRandom(125)+25);
ag^.Pos:= Gear^.Pos;
ag^.Power:= Gear^.Power
end;
@@ -853,7 +857,7 @@
Gear^.State:= Gear^.State and (not gstMoving);
exit
end;
-isFalling:= (Gear^.dY.isNegative) or not TestCollisionYKick(Gear, 1);
+isFalling:= (Gear^.dY.isNegative) or (not TestCollisionYKick(Gear, 1));
if isFalling then
begin
if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then
@@ -983,7 +987,7 @@
begin
Gear^.State:= Gear^.State and (not gstWinner);
Gear^.State:= Gear^.State and (not gstMoving);
- while (TestCollisionYWithGear(Gear,1) = 0) and not CheckGearDrowning(Gear) do
+ while (TestCollisionYWithGear(Gear,1) = 0) and (not CheckGearDrowning(Gear)) do
Gear^.Y:= Gear^.Y+_1;
SetLittle(Gear^.dX);
Gear^.dY:= _0
@@ -1010,8 +1014,8 @@
if (CurrentHedgehog^.Gear = Gear) then
isCursorVisible:= false
end;
-
-if (hwAbs(Gear^.dY) > _0) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then
+// IMO this should trigger homerun based on leftX/rightX + someval instead - that is 'knocking it out of the park'
+if (not isZero(Gear^.dY)) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then
begin
inc(Gear^.FlightTime);
if Gear^.FlightTime = 3000 then
--- a/hedgewars/uGearsList.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsList.pas Thu Aug 30 13:02:19 2012 -0400
@@ -105,6 +105,7 @@
// Define ammo association, if any.
gear^.AmmoType:= GearKindAmmoTypeMap[Kind];
gear^.CollisionMask:= $FFFF;
+gear^.stepFreq:= 1;
if CurrentHedgehog <> nil then gear^.Hedgehog:= CurrentHedgehog;
@@ -230,7 +231,7 @@
gear^.Radius:= 2;
gear^.Elasticity:= _0_55;
gear^.Friction:= _0_995;
- gear^.Density:= _0_9;
+ gear^.Density:= _1;
if cMinesTime < 0 then
gear^.Timer:= getrandom(51)*100
else
@@ -242,7 +243,7 @@
gear^.Radius:= 2;
gear^.Elasticity:= _0_55;
gear^.Friction:= _0_995;
- gear^.Density:= _0_9;
+ gear^.Density:= _1_6;
gear^.Timer:= 500;
end;
gtCase: begin
--- a/hedgewars/uGearsRender.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsRender.pas Thu Aug 30 13:02:19 2012 -0400
@@ -376,26 +376,26 @@
hAngle:= 0;
i:= -1
end;
- if ((Gear^.State and gstWinner) = 0) then
- begin
- DrawHedgehog(ox, oy,
- i,
- 1,
- 0,
- DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + dAngle);
- with HH^ do
- if (HatTex <> nil) then
- begin
- DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 0, i, 32, 32,
- i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
- if HatTex^.w > 64 then
+ if ((Gear^.State and gstWinner) = 0) then
+ begin
+ DrawHedgehog(ox, oy,
+ i,
+ 1,
+ 0,
+ DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + dAngle);
+ with HH^ do
+ if (HatTex <> nil) then
begin
- Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
- DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 32, i, 32, 32,
+ DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 0, i, 32, 32,
i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
- Tint($FF, $FF, $FF, $FF)
+ if HatTex^.w > 64 then
+ begin
+ Tint(HH^.Team^.Clan^.Color shl 8 or $FF);
+ DrawTextureRotatedF(HatTex, 1.0, -1.0, -6.0, ox, oy, 32, i, 32, 32,
+ i*DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + hAngle);
+ Tint($FF, $FF, $FF, $FF)
+ end
end
- end
end;
DrawAltWeapon(Gear, ox, oy);
defaultPos:= false
--- a/hedgewars/uGearsUtils.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uGearsUtils.pas Thu Aug 30 13:02:19 2012 -0400
@@ -38,6 +38,8 @@
function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
function CheckGearDrowning(Gear: PGear): boolean;
+procedure CheckCollision(Gear: PGear); inline;
+procedure CheckCollisionWithLand(Gear: PGear); inline;
var doStepHandlers: array[TGearType] of TGearStepProcedure;
@@ -106,7 +108,7 @@
// Run the calcs only once we know we have a type that will need damage
tdX:= Gear^.X-fX;
tdY:= Gear^.Y-fY;
- if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then
+ if (tdX.Round + tdY.Round + 2) < dmgBase then
dmg:= dmgBase - hwRound(Distance(tdX, tdY));
if dmg > 1 then
begin
@@ -140,7 +142,7 @@
// Run the calcs only once we know we have a type that will need damage
tdX:= Gear^.X-fX;
tdY:= Gear^.Y-fY;
- if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then
+ if (tdX.Round + tdY.Round + 2) < dmgBase then
dmg:= dmgBase - hwRound(Distance(tdX, tdY));
if dmg > 1 then
begin
@@ -239,9 +241,7 @@
end;
end
else if Gear^.Kind <> gtStructure then // not gtHedgehog nor gtStructure
- begin
Gear^.Hedgehog:= AttackerHog;
- end;
inc(Gear^.Damage, Damage);
ScriptCall('onGearDamage', Gear^.UID, Damage);
@@ -319,7 +319,8 @@
var
dAngle: real;
begin
- dAngle := (Gear^.dX.QWordValue + Gear^.dY.QWordValue) / $80000000;
+// Frac/Round to be kind to JS as of 2012-08-27 where there is yet no int64/uint64
+ dAngle := (Gear^.dX.Round + Gear^.dY.Round) / 2 + (Gear^.dX.Frac+Gear^.dY.Frac) / $80000000;
if not Gear^.dX.isNegative then
Gear^.DirAngle := Gear^.DirAngle + dAngle
else
@@ -420,10 +421,9 @@
Scale:= hwFloat2Float(Gear^.Density / _3 * Gear^.dY);
if Scale > 1 then Scale:= power(Scale,0.3333)
else Scale:= Scale + ((1-Scale) / 2);
- if Scale > 1 then Timer:= round(max(Scale,3))
+ if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
else Timer:= 1;
// Low Gravity
- Timer:=round(0.0005/cGravityf);
FrameTicks:= FrameTicks*Timer;
end;
@@ -604,7 +604,7 @@
inc(cnt)
end;
- inc(y, 45)
+ inc(y, 10)
end;
if cnt > 0 then
@@ -664,4 +664,22 @@
CheckGearNear:= nil
end;
+procedure CheckCollision(Gear: PGear); inline;
+begin
+ if TestCollisionXwithGear(Gear, hwSign(Gear^.dX))
+ or (TestCollisionYwithGear(Gear, hwSign(Gear^.dY)) <> 0) then
+ Gear^.State := Gear^.State or gstCollision
+ else
+ Gear^.State := Gear^.State and (not gstCollision)
+end;
+
+procedure CheckCollisionWithLand(Gear: PGear); inline;
+begin
+ if TestCollisionX(Gear, hwSign(Gear^.dX))
+ or TestCollisionY(Gear, hwSign(Gear^.dY)) then
+ Gear^.State := Gear^.State or gstCollision
+ else
+ Gear^.State := Gear^.State and (not gstCollision)
+end;
+
end.
--- a/hedgewars/uIO.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uIO.pas Thu Aug 30 13:02:19 2012 -0400
@@ -126,6 +126,7 @@
'D': GameType:= gmtDemo;
'N': GameType:= gmtNet;
'S': GameType:= gmtSave;
+ 'V': GameType:= gmtRecord;
else OutError(errmsgIncorrectUse + ' IPC "T" :' + s[2], true) end;
'V': begin
if s[2] = '.' then
@@ -406,7 +407,7 @@
TargetPoint.Y:= putY
end;
AddFileLog('put: ' + inttostr(TargetPoint.X) + ', ' + inttostr(TargetPoint.Y));
- State:= State and not gstHHChooseTarget;
+ State:= State and (not gstHHChooseTarget);
if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackingPut) <> 0 then
Message:= Message or (gmAttack and InputMask);
end
--- a/hedgewars/uInputHandler.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uInputHandler.pas Thu Aug 30 13:02:19 2012 -0400
@@ -25,8 +25,9 @@
procedure initModule;
procedure freeModule;
-function KeyNameToCode(name: shortstring; Modifier: shortstring = ''): LongInt;
-procedure MaskModifier(var code: LongInt; modifier: LongWord);
+function KeyNameToCode(name: shortstring): LongInt; inline;
+function KeyNameToCode(name: shortstring; Modifier: shortstring): LongInt;
+//procedure MaskModifier(var code: LongInt; modifier: LongWord);
procedure MaskModifier(Modifier: shortstring; var code: LongInt);
procedure ProcessMouse(event: TSDL_MouseButtonEvent; ButtonDown: boolean);
procedure ProcessKey(event: TSDL_KeyboardEvent); inline;
@@ -60,6 +61,11 @@
KeyNames: array [0..cKeyMaxIndex] of string[15];
CurrentBinds: TBinds;
+function KeyNameToCode(name: shortstring): LongInt; inline;
+begin
+ KeyNameToCode:= KeyNameToCode(name, '');
+end;
+
function KeyNameToCode(name: shortstring; Modifier: shortstring): LongInt;
var code: LongInt;
begin
@@ -70,7 +76,7 @@
MaskModifier(Modifier, code);
KeyNameToCode:= code;
end;
-
+(*
procedure MaskModifier(var code: LongInt; Modifier: LongWord);
begin
if(Modifier and KMOD_LSHIFT) <> 0 then code:= code or LSHIFT;
@@ -80,7 +86,7 @@
if(Modifier and KMOD_LCTRL) <> 0 then code:= code or LCTRL;
if(Modifier and KMOD_RCTRL) <> 0 then code:= code or LCTRL;
end;
-
+*)
procedure MaskModifier(Modifier: shortstring; var code: LongInt);
var mod_ : shortstring;
ModifierCount, i: LongInt;
@@ -133,7 +139,7 @@
if CurrentBinds[code][0] <> #0 then
begin
- if (code > 3) and KeyDown and not ((CurrentBinds[code] = 'put') or (CurrentBinds[code] = 'ammomenu') or (CurrentBinds[code] = '+cur_u') or (CurrentBinds[code] = '+cur_d') or (CurrentBinds[code] = '+cur_l') or (CurrentBinds[code] = '+cur_r')) then hideAmmoMenu:= true;
+ if (code > 3) and KeyDown and (not ((CurrentBinds[code] = 'put')) or (CurrentBinds[code] = 'ammomenu') or (CurrentBinds[code] = '+cur_u') or (CurrentBinds[code] = '+cur_d') or (CurrentBinds[code] = '+cur_l') or (CurrentBinds[code] = '+cur_r')) then hideAmmoMenu:= true;
if KeyDown then
begin
@@ -250,6 +256,7 @@
DefaultBinds[KeyNameToCode(_S'9')]:= '+voldown';
DefaultBinds[KeyNameToCode(_S'8')]:= 'mute';
DefaultBinds[KeyNameToCode(_S'c')]:= 'capture';
+DefaultBinds[KeyNameToCode(_S'r')]:= 'record';
DefaultBinds[KeyNameToCode(_S'h')]:= 'findhh';
DefaultBinds[KeyNameToCode(_S'p')]:= 'pause';
DefaultBinds[KeyNameToCode(_S's')]:= '+speedup';
--- a/hedgewars/uLand.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLand.pas Thu Aug 30 13:02:19 2012 -0400
@@ -20,7 +20,7 @@
unit uLand;
interface
-uses SDLh, uLandTemplates, uFloat, uConsts, GLunit, uTypes;
+uses SDLh, uLandTemplates, uFloat, uConsts, GLunit, uTypes, uAILandMarks;
procedure initModule;
procedure freeModule;
@@ -35,6 +35,28 @@
var digest: shortstring;
+procedure ResizeLand(width, height: LongWord);
+var potW, potH: LongWord;
+begin
+potW:= toPowerOf2(width);
+potH:= toPowerOf2(height);
+if (potW <> LAND_WIDTH) or (potH <> LAND_HEIGHT) then
+ begin
+ LAND_WIDTH:= potW;
+ LAND_HEIGHT:= potH;
+ LAND_WIDTH_MASK:= not(LAND_WIDTH-1);
+ LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1);
+ cWaterLine:= LAND_HEIGHT;
+ if (cReducedQuality and rqBlurryLand) = 0 then
+ SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH)
+ else
+ SetLength(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2);
+
+ SetLength(Land, LAND_HEIGHT, LAND_WIDTH);
+ SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32));
+ end;
+end;
+
procedure ColorizeLand(Surface: PSDL_Surface);
var tmpsurf: PSDL_Surface;
r, rr: TSDL_Rect;
@@ -181,6 +203,7 @@
i: Longword;
y, x: Longword;
begin
+ ResizeLand(Template.TemplateWidth, Template.TemplateHeight);
for y:= 0 to LAND_HEIGHT - 1 do
for x:= 0 to LAND_WIDTH - 1 do
Land[y, x]:= lfBasic;
@@ -237,6 +260,7 @@
procedure GenDrawnMap;
begin
+ ResizeLand(4096, 2048);
uLandPainted.Draw;
MaxHedgehogs:= 48;
@@ -260,6 +284,11 @@
3: SelectTemplate:= LargeTemplates[getrandom(Succ(High(LargeTemplates)))];
4: SelectTemplate:= CavernTemplates[getrandom(Succ(High(CavernTemplates)))];
5: SelectTemplate:= WackyTemplates[getrandom(Succ(High(WackyTemplates)))];
+// For lua only!
+ 6: begin
+ SelectTemplate:= min(LuaTemplateNumber,High(EdgeTemplates));
+ GetRandom(2) // burn 1
+ end;
end;
WriteLnToConsole('Selected template #'+inttostr(SelectTemplate)+' using filter #'+inttostr(cTemplateFilter));
@@ -299,7 +328,7 @@
WriteLnToConsole('Generating land...');
case cMapGen of
0: GenBlank(EdgeTemplates[SelectTemplate]);
- 1: GenMaze;
+ 1: begin ResizeLand(4096,2048); GenMaze; end;
2: GenDrawnMap;
else
OutError('Unknown mapgen', true);
@@ -489,7 +518,10 @@
if tmpsurf = nil then
tmpsurf:= LoadImage(Pathz[ptMissionMaps] + '/' + mapName + '/map', ifAlpha or ifCritical or ifTransparent or ifIgnoreCaps);
end;
-TryDo((tmpsurf^.w <= LAND_WIDTH) and (tmpsurf^.h <= LAND_HEIGHT), 'Map dimensions too big!', true);
+// (bare) Sanity check. Considering possible LongInt comparisons as well as just how much system memoery it would take
+TryDo((tmpsurf^.w < $40000000) and (tmpsurf^.h < $40000000) and (tmpsurf^.w * tmpsurf^.h < 6*1024*1024*1024), 'Map dimensions too big!', true);
+
+ResizeLand(tmpsurf^.w, tmpsurf^.h);
// unC0Rr - should this be passed from the GUI? I am not sure which layer does what
s:= UserPathz[ptMapCurrent] + '/map.cfg';
@@ -581,7 +613,7 @@
if Land[y, x] <> 0 then
begin
inc(c);
- if c > 200 then // avoid accidental triggering
+ if c > 1000 then // avoid accidental triggering
begin
hasBorder:= true;
break;
@@ -676,24 +708,35 @@
LandPixels[y,x]:= w or (LandPixels[y div 2, x div 2] and AMask)
end
end;
-
-UpdateLandTexture(0, LAND_WIDTH, 0, LAND_HEIGHT, false);
end;
procedure GenPreview(out Preview: TPreview);
-var x, y, xx, yy, t, bit, cbit, lh, lw: LongInt;
+var rh, rw, ox, oy, x, y, xx, yy, t, bit, cbit, lh, lw: LongInt;
begin
WriteLnToConsole('Generating preview...');
case cMapGen of
0: GenBlank(EdgeTemplates[SelectTemplate]);
- 1: GenMaze;
+ 1: begin ResizeLand(4096,2048); GenMaze; end;
2: GenDrawnMap;
else
OutError('Unknown mapgen', true);
end;
- lh:= LAND_HEIGHT div 128;
- lw:= LAND_WIDTH div 32;
+ // strict scaling needed here since preview assumes a rectangle
+ rh:= max(LAND_HEIGHT,2048);
+ rw:= max(LAND_WIDTH,4096);
+ ox:= 0;
+ if rw < rh*2 then
+ begin
+ rw:= rh*2;
+ end;
+ if rh < rw div 2 then rh:= rw * 2;
+
+ ox:= (rw-LAND_WIDTH) div 2;
+ oy:= rh-LAND_HEIGHT;
+
+ lh:= rh div 128;
+ lw:= rw div 32;
for y:= 0 to 127 do
for x:= 0 to 31 do
begin
@@ -704,7 +747,8 @@
cbit:= bit * 8;
for yy:= y * lh to y * lh + 7 do
for xx:= x * lw + cbit to x * lw + cbit + 7 do
- if Land[yy, xx] <> 0 then
+ if ((yy-oy) and LAND_HEIGHT_MASK = 0) and ((xx-ox) and LAND_WIDTH_MASK = 0)
+ and (Land[yy-oy, xx-ox] <> 0) then
inc(t);
if t > 8 then
Preview[y, x]:= Preview[y, x] or ($80 shr bit);
@@ -741,7 +785,9 @@
LandBackSurface:= nil;
digest:= '';
-
+ LAND_WIDTH:= 0;
+ LAND_HEIGHT:= 0;
+(*
if (cReducedQuality and rqBlurryLand) = 0 then
SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH)
else
@@ -749,6 +795,7 @@
SetLength(Land, LAND_HEIGHT, LAND_WIDTH);
SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32));
+*)
end;
procedure freeModule;
--- a/hedgewars/uLandGraphics.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandGraphics.pas Thu Aug 30 13:02:19 2012 -0400
@@ -59,7 +59,7 @@
begin
addBgColor:= NewColor;
exit
- end;
+ end;
// Get colors
oRed := (OldColor shr RShift);
oGreen := (OldColor shr GShift);
@@ -72,7 +72,7 @@
// Mix colors
nRed := min(255,((nRed*nAlpha) div 255) + ((oRed*oAlpha*byte(255-nAlpha)) div 65025));
nGreen := min(255,((nGreen*nAlpha) div 255) + ((oGreen*oAlpha*byte(255-nAlpha)) div 65025));
- nBlue := min(255,((nBlue*nAlpha) div 255) + ((oBlue*oAlpha*byte(255-nAlpha)) div 65025));
+ nBlue := min(255,((nBlue*nAlpha) div 255) + ((oBlue*oAlpha*byte(255-nAlpha)) div 65025));
nAlpha := min(255, oAlpha + nAlpha);
addBgColor := (nAlpha shl AShift) or (nRed shl RShift) or (nGreen shl GShift) or (nBlue shl BShift);
@@ -106,25 +106,25 @@
begin
if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
- if isCurrent then
+ if isCurrent then
Land[y + dy, i]:= Land[y + dy, i] and $FF7F
else if Land[y + dy, i] and $007F > 0 then
Land[y + dy, i]:= (Land[y + dy, i] and $FF80) or ((Land[y + dy, i] and $7F) - 1);
if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
- if isCurrent then
+ if isCurrent then
Land[y - dy, i]:= Land[y - dy, i] and $FF7F
else if Land[y - dy, i] and $007F > 0 then
Land[y - dy, i]:= (Land[y - dy, i] and $FF80) or ((Land[y - dy, i] and $7F) - 1);
if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
- if isCurrent then
+ if isCurrent then
Land[y + dx, i]:= Land[y + dx, i] and $FF7F
else if Land[y + dx, i] and $007F > 0 then
Land[y + dx, i]:= (Land[y + dx, i] and $FF80) or ((Land[y + dx, i] and $7F) - 1);
if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
- if isCurrent then
+ if isCurrent then
Land[y - dx, i]:= Land[y - dx, i] and $FF7F
else if Land[y - dx, i] and $007F > 0 then
Land[y - dx, i]:= (Land[y - dx, i] and $FF80) or ((Land[y - dx, i] and $7F) - 1)
@@ -133,27 +133,27 @@
begin
if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
- if isCurrent then
+ if isCurrent then
Land[y + dy, i]:= Land[y + dy, i] or $80
else if Land[y + dy, i] and $007F < 127 then
Land[y + dy, i]:= (Land[y + dy, i] and $FF80) or ((Land[y + dy, i] and $7F) + 1);
- if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
- for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
- if isCurrent then
- Land[y - dy, i]:= Land[y - dy, i] or $80
- else if Land[y - dy, i] and $007F < 127 then
+ if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
+ for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
+ if isCurrent then
+ Land[y - dy, i]:= Land[y - dy, i] or $80
+ else if Land[y - dy, i] and $007F < 127 then
Land[y - dy, i]:= (Land[y - dy, i] and $FF80) or ((Land[y - dy, i] and $7F) + 1);
- if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
- for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
- if isCurrent then
- Land[y + dx, i]:= Land[y + dx, i] or $80
- else if Land[y + dx, i] and $007F < 127 then
+ if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
+ for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
+ if isCurrent then
+ Land[y + dx, i]:= Land[y + dx, i] or $80
+ else if Land[y + dx, i] and $007F < 127 then
Land[y + dx, i]:= (Land[y + dx, i] and $FF80) or ((Land[y + dx, i] and $7F) + 1);
- if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
- for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
- if isCurrent then
- Land[y - dx, i]:= Land[y - dx, i] or $80
- else if Land[y - dx, i] and $007F < 127 then
+ if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
+ for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
+ if isCurrent then
+ Land[y - dx, i]:= Land[y - dx, i] or $80
+ else if Land[y - dx, i] and $007F < 127 then
Land[y - dx, i]:= (Land[y - dx, i] and $FF80) or ((Land[y - dx, i] and $7F) + 1)
end
end;
@@ -266,7 +266,7 @@
inc(cnt);
LandPixels[by, bx]:= LandBackPixel(i, t)
end
- else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
+ else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
LandPixels[by, bx]:= 0
end;
@@ -288,7 +288,7 @@
inc(cnt);
LandPixels[by, bx]:= LandBackPixel(i, t)
end
- else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
+ else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
LandPixels[by, bx]:= 0
end;
@@ -310,7 +310,7 @@
inc(cnt);
LandPixels[by, bx]:= LandBackPixel(i, t)
end
- else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
+ else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
LandPixels[by, bx]:= 0
end;
t:= y - dx;
@@ -331,7 +331,7 @@
inc(cnt);
LandPixels[by, bx]:= LandBackPixel(i, t)
end
- else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
+ else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
LandPixels[by, bx]:= 0
end;
FillLandCircleLinesBG:= cnt;
@@ -504,7 +504,7 @@
end;
if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
LandPixels[by, bx]:= LandBackPixel(tx, ty)
- else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
+ else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
LandPixels[by, bx]:= 0
end
end;
@@ -567,7 +567,7 @@
and ((tx and LAND_WIDTH_MASK) = 0)
and (((Land[ty, tx] and lfBasic) <> 0) or ((Land[ty, tx] and lfObject) <> 0)) then
begin
- if despeckle then
+ if despeckle then
begin
Land[ty, tx]:= Land[ty, tx] or lfDamaged;
LandDirty[ty div 32, tx div 32]:= 1
@@ -837,7 +837,7 @@
procedure Smooth(X, Y: LongInt);
begin
// a bit of AA for explosions
-if (Land[Y, X] = 0) and (Y > LongInt(topY) + 1) and
+if (Land[Y, X] = 0) and (Y > LongInt(topY) + 1) and
(Y < LAND_HEIGHT-2) and (X > LongInt(leftX) + 1) and (X < LongInt(rightX) - 1) then
begin
if ((((Land[y, x-1] and lfDamaged) <> 0) and (((Land[y+1,x] and lfDamaged) <> 0)) or ((Land[y-1,x] and lfDamaged) <> 0))
--- a/hedgewars/uLandObjects.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandObjects.pas Thu Aug 30 13:02:19 2012 -0400
@@ -501,7 +501,7 @@
c2.g:= t;
c2.b:= t
end;
- ExplosionBorderColor:= c2.value or AMask;
+ ExplosionBorderColor:= (c2.r shl RShift) or (c2.g shl GShift) or (c2.b shl BShift) or AMask;
end
else if key = 'water-top' then
begin
@@ -554,7 +554,7 @@
SetMusicName(Trim(s))
else if key = 'clouds' then
begin
- cCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div LAND_WIDTH;
+ cCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div 4096;
cSDCloudsNumber:= cCloudsNumber
end
else if key = 'object' then
@@ -700,7 +700,7 @@
else if key = 'sd-water-opacity' then
SDWaterOpacity:= StrToInt(Trim(s))
else if key = 'sd-clouds' then
- cSDCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div LAND_WIDTH
+ cSDCloudsNumber:= Word(StrToInt(Trim(s))) * cScreenSpace div 4096
else if key = 'sd-flakes' then
begin
i:= Pos(',', s);
--- a/hedgewars/uLandPainted.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandPainted.pas Thu Aug 30 13:02:19 2012 -0400
@@ -58,9 +58,9 @@
rec.X:= SDLNet_Read16(@rec.X);
rec.Y:= SDLNet_Read16(@rec.Y);
if rec.X < -318 then rec.X:= -318;
- if rec.X > LAND_WIDTH+318 then rec.X:= LAND_WIDTH+318;
+ if rec.X > 4096+318 then rec.X:= 4096+318;
if rec.Y < -318 then rec.Y:= -318;
- if rec.Y > LAND_HEIGHT+318 then rec.Y:= LAND_HEIGHT+318;
+ if rec.Y > 2048+318 then rec.Y:= 2048+318;
new(pe);
if pointsListLast = nil then
--- a/hedgewars/uLandTemplates.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uLandTemplates.pas Thu Aug 30 13:02:19 2012 -0400
@@ -1457,92 +1457,92 @@
// Many islands
const Template41Points: array[0..86] of TSDL_Rect =
(
- (x: 95; y: 500; w: 1; h: 1),
- (x: 100; y: 275; w: 25; h: 100),
- (x: 325; y: 275; w: 25; h: 100),
- (x: 330; y: 500; w: 1; h: 1),
+ (x: 95; y: 500; w: 26; h: 26),
+ (x: 100; y: 275; w: 50; h: 125),
+ (x: 325; y: 275; w: 50; h: 125),
+ (x: 330; y: 500; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 725; y: 125; w: 1; h: 1),
- (x: 725; y: 25; w: 5; h: 25),
- (x: 825; y: 35; w: 5; h: 10),
- (x: 825; y: 135; w: 1; h: 1),
+ (x: 725; y: 125; w: 26; h: 26),
+ (x: 725; y: 25; w: 30; h: 50),
+ (x: 825; y: 35; w: 30; h: 35),
+ (x: 825; y: 135; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 1150; y: 550; w: 25; h: 50),
- (x: 1250; y: 300; w: 25; h: 50),
- (x: 1350; y: 300; w: 25; h: 50),
- (x: 1400; y: 575; w: 25; h: 50),
+ (x: 1150; y: 550; w: 50; h: 75),
+ (x: 1250; y: 300; w: 50; h: 75),
+ (x: 1350; y: 300; w: 50; h: 75),
+ (x: 1400; y: 575; w: 50; h: 75),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 525; y:1050; w: 50; h: 50),
- (x: 700; y: 800; w: 100; h: 150),
- (x: 950; y: 900; w: 100; h: 150),
- (x: 1100; y:1100; w: 50; h: 50),
+ (x: 525; y:1050; w: 75; h: 75),
+ (x: 700; y: 800; w: 125; h: 175),
+ (x: 950; y: 900; w: 125; h: 175),
+ (x: 1100; y:1100; w: 75; h: 75),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 175; y:1500; w: 1; h: 1),
- (x: 210; y:1400; w: 5; h: 25),
- (x: 240; y:1400; w: 5; h: 25),
- (x: 275; y:1510; w: 1; h: 1),
+ (x: 175; y:1500; w: 26; h: 26),
+ (x: 210; y:1400; w: 30; h: 50),
+ (x: 240; y:1400; w: 30; h: 50),
+ (x: 275; y:1510; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 450; y:1800; w: 100; h: 100),
- (x: 600; y:1750; w: 100; h: 100),
- (x: 750; y:1750; w: 100; h: 100),
- (x: 950; y:1850; w: 100; h: 100),
+ (x: 450; y:1800; w: 125; h: 125),
+ (x: 600; y:1750; w: 125; h: 125),
+ (x: 750; y:1750; w: 125; h: 125),
+ (x: 950; y:1850; w: 125; h: 125),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 1075; y:1450; w: 1; h: 1),
- (x: 1110; y:1300; w: 5; h: 25),
- (x: 1140; y:1300; w: 5; h: 25),
- (x: 1175; y:1430; w: 1; h: 1),
+ (x: 1075; y:1450; w: 26; h: 26),
+ (x: 1110; y:1300; w: 30; h: 50),
+ (x: 1140; y:1300; w: 30; h: 50),
+ (x: 1175; y:1430; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 1600; y:1250; w: 25; h: 100),
- (x: 1700; y:1150; w: 25; h: 100),
- (x: 1850; y: 500; w: 50; h: 100),
- (x: 1950; y: 550; w: 50; h: 150),
- (x: 2250; y:1150; w: 25; h: 100),
- (x: 2350; y:1250; w: 25; h: 100),
+ (x: 1600; y:1250; w: 50; h: 125),
+ (x: 1700; y:1150; w: 50; h: 125),
+ (x: 1850; y: 500; w: 75; h: 125),
+ (x: 1950; y: 550; w: 75; h: 175),
+ (x: 2250; y:1150; w: 50; h: 125),
+ (x: 2350; y:1250; w: 50; h: 125),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 1750; y:2010; w: 1; h: 1),
- (x: 1900; y:1870; w: 50; h: 50),
- (x: 2050; y:1870; w: 50; h: 50),
- (x: 2175; y:2010; w: 1; h: 1),
+ (x: 1750; y:2010; w: 26; h: 26),
+ (x: 1900; y:1870; w: 75; h: 75),
+ (x: 2050; y:1870; w: 75; h: 75),
+ (x: 2175; y:2010; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 2500; y:1700; w: 1; h: 1),
- (x: 2575; y:1500; w: 10; h: 50),
- (x: 2650; y:1500; w: 10; h: 50),
- (x: 2700; y:1690; w: 1; h: 1),
+ (x: 2500; y:1700; w: 26; h: 26),
+ (x: 2575; y:1500; w: 35; h: 75),
+ (x: 2650; y:1500; w: 35; h: 75),
+ (x: 2700; y:1690; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 2000; y: 125; w: 1; h: 1),
- (x: 2050; y: 50; w: 25; h: 25),
- (x: 2100; y: 50; w: 25; h: 25),
- (x: 2150; y: 150; w: 1; h: 1),
+ (x: 2000; y: 125; w: 26; h: 26),
+ (x: 2050; y: 50; w: 50; h: 50),
+ (x: 2100; y: 50; w: 50; h: 50),
+ (x: 2150; y: 150; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 2600; y: 250; w: 25; h: 100),
- (x: 2750; y: 400; w: 50; h: 50),
- (x: 2900; y: 525; w: 50; h: 50),
- (x: 3150; y: 550; w: 50; h: 100),
+ (x: 2600; y: 250; w: 50; h: 125),
+ (x: 2750; y: 400; w: 75; h: 75),
+ (x: 2900; y: 525; w: 75; h: 75),
+ (x: 3150; y: 550; w: 75; h: 125),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 2800; y:1150; w: 1; h: 1),
- (x: 2840; y: 950; w: 25; h: 25),
- (x: 2880; y: 950; w: 25; h: 25),
- (x: 2900; y:1150; w: 1; h: 1),
+ (x: 2800; y:1150; w: 26; h: 26),
+ (x: 2840; y: 950; w: 50; h: 50),
+ (x: 2880; y: 950; w: 50; h: 50),
+ (x: 2900; y:1150; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 3075; y:1985; w: 1; h: 1),
- (x: 3325; y:1700; w: 50; h: 100),
- (x: 3475; y:1700; w: 50; h: 100),
- (x: 3625; y:1985; w: 1; h: 1),
+ (x: 3075; y:1985; w: 26; h: 26),
+ (x: 3325; y:1700; w: 75; h: 125),
+ (x: 3475; y:1700; w: 75; h: 125),
+ (x: 3625; y:1985; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 3200; y:1450; w: 1; h: 1),
- (x: 3240; y:1350; w: 25; h: 25),
- (x: 3280; y:1350; w: 25; h: 25),
- (x: 3300; y:1450; w: 1; h: 1),
+ (x: 3200; y:1450; w: 26; h: 26),
+ (x: 3240; y:1350; w: 50; h: 50),
+ (x: 3280; y:1350; w: 50; h: 50),
+ (x: 3300; y:1450; w: 26; h: 26),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 3500; y:1050; w: 25; h: 50),
- (x: 3650; y: 600; w: 50; h: 100),
- (x: 3800; y: 600; w: 50; h: 100),
- (x: 3900; y:1000; w: 25; h: 50),
+ (x: 3500; y:1050; w: 50; h: 75),
+ (x: 3650; y: 600; w: 75; h: 125),
+ (x: 3800; y: 600; w: 75; h: 125),
+ (x: 3900; y:1000; w: 50; h: 75),
(x: NTPX; y: 0; w: 1; h: 1),
- (x: 3800; y: 200; w: 25; h: 50),
- (x: 3875; y: 100; w: 50; h: 50),
- (x: 3925; y: 50; w: 50; h: 25),
- (x: 4050; y: 125; w: 25; h: 50),
+ (x: 3800; y: 200; w: 50; h: 75),
+ (x: 3875; y: 100; w: 75; h: 75),
+ (x: 3925; y: 50; w: 75; h: 50),
+ (x: 4050; y: 125; w: 50; h: 75),
(x: NTPX; y: 0; w: 1; h: 1)
);
Template41FPoints: array[0..0] of TPoint =
@@ -1571,9 +1571,191 @@
(
(X: 512; Y: 0)
);
+// Many islands
+const Template43Points: array[0..173] of TSDL_Rect =
+ (
+ (x: 95; y: 500; w: 26; h: 26),
+ (x: 100; y: 275; w: 50; h: 125),
+ (x: 325; y: 275; w: 50; h: 125),
+ (x: 330; y: 500; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 725; y: 125; w: 26; h: 26),
+ (x: 725; y: 25; w: 30; h: 50),
+ (x: 825; y: 35; w: 30; h: 35),
+ (x: 825; y: 135; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 1150; y: 550; w: 50; h: 75),
+ (x: 1250; y: 300; w: 50; h: 75),
+ (x: 1350; y: 300; w: 50; h: 75),
+ (x: 1400; y: 575; w: 50; h: 75),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 525; y:1050; w: 75; h: 75),
+ (x: 700; y: 800; w: 125; h: 175),
+ (x: 950; y: 900; w: 125; h: 175),
+ (x: 1100; y:1100; w: 75; h: 75),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 175; y:1500; w: 26; h: 26),
+ (x: 210; y:1400; w: 30; h: 50),
+ (x: 240; y:1400; w: 30; h: 50),
+ (x: 275; y:1510; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 450; y:1800; w: 125; h: 125),
+ (x: 600; y:1750; w: 125; h: 125),
+ (x: 750; y:1750; w: 125; h: 125),
+ (x: 950; y:1850; w: 125; h: 125),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 1075; y:1450; w: 26; h: 26),
+ (x: 1110; y:1300; w: 30; h: 50),
+ (x: 1140; y:1300; w: 30; h: 50),
+ (x: 1175; y:1430; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 1600; y:1250; w: 50; h: 125),
+ (x: 1700; y:1150; w: 50; h: 125),
+ (x: 1850; y: 500; w: 75; h: 125),
+ (x: 1950; y: 550; w: 75; h: 175),
+ (x: 2250; y:1150; w: 50; h: 125),
+ (x: 2350; y:1250; w: 50; h: 125),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 1750; y:2010; w: 26; h: 26),
+ (x: 1900; y:1870; w: 75; h: 75),
+ (x: 2050; y:1870; w: 75; h: 75),
+ (x: 2175; y:2010; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 2500; y:1700; w: 26; h: 26),
+ (x: 2575; y:1500; w: 35; h: 75),
+ (x: 2650; y:1500; w: 35; h: 75),
+ (x: 2700; y:1690; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 2000; y: 125; w: 26; h: 26),
+ (x: 2050; y: 50; w: 50; h: 50),
+ (x: 2100; y: 50; w: 50; h: 50),
+ (x: 2150; y: 150; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 2600; y: 250; w: 50; h: 125),
+ (x: 2750; y: 400; w: 75; h: 75),
+ (x: 2900; y: 525; w: 75; h: 75),
+ (x: 3150; y: 550; w: 75; h: 125),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 2800; y:1150; w: 26; h: 26),
+ (x: 2840; y: 950; w: 50; h: 50),
+ (x: 2880; y: 950; w: 50; h: 50),
+ (x: 2900; y:1150; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 3075; y:1985; w: 26; h: 26),
+ (x: 3325; y:1700; w: 75; h: 125),
+ (x: 3475; y:1700; w: 75; h: 125),
+ (x: 3625; y:1985; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 3200; y:1450; w: 26; h: 26),
+ (x: 3240; y:1350; w: 50; h: 50),
+ (x: 3280; y:1350; w: 50; h: 50),
+ (x: 3300; y:1450; w: 26; h: 26),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 3500; y:1050; w: 50; h: 75),
+ (x: 3650; y: 600; w: 75; h: 125),
+ (x: 3800; y: 600; w: 75; h: 125),
+ (x: 3900; y:1000; w: 50; h: 75),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 3800; y: 200; w: 50; h: 75),
+ (x: 3875; y: 100; w: 75; h: 75),
+ (x: 3925; y: 50; w: 75; h: 50),
+ (x: 4050; y: 125; w: 50; h: 75),
+ (x: NTPX; y: 0; w: 1; h: 1),
+ (x: 95; y:2548; w: 26; h: 26),
+ (x: 100; y:2323; w: 50; h: 125),
+ (x: 325; y:2323; w: 50; h: 125),
+ (x: 330; y:2548; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 725; y:2173; w: 26; h: 26),
+ (x: 725; y:2073; w: 30; h: 50),
+ (x: 825; y:2083; w: 30; h: 35),
+ (x: 825; y:2183; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 1150; y:2598; w: 50; h: 75),
+ (x: 1250; y:2348; w: 50; h: 75),
+ (x: 1350; y:2348; w: 50; h: 75),
+ (x: 1400; y:2623; w: 50; h: 75),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 525; y:3098; w: 75; h: 75),
+ (x: 700; y:2848; w: 125; h: 175),
+ (x: 950; y:2948; w: 125; h: 175),
+ (x: 1100; y:3148; w: 75; h: 75),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 175; y:3548; w: 26; h: 26),
+ (x: 210; y:3448; w: 30; h: 50),
+ (x: 240; y:3448; w: 30; h: 50),
+ (x: 275; y:3558; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 450; y:3848; w: 125; h: 125),
+ (x: 600; y:3798; w: 125; h: 125),
+ (x: 750; y:3798; w: 125; h: 125),
+ (x: 950; y:3898; w: 125; h: 125),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 1075; y:3498; w: 26; h: 26),
+ (x: 1110; y:3348; w: 30; h: 50),
+ (x: 1140; y:3348; w: 30; h: 50),
+ (x: 1175; y:3478; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 1600; y:3298; w: 50; h: 125),
+ (x: 1700; y:3198; w: 50; h: 125),
+ (x: 1850; y:2548; w: 75; h: 125),
+ (x: 1950; y:2598; w: 75; h: 175),
+ (x: 2250; y:3198; w: 50; h: 125),
+ (x: 2350; y:3298; w: 50; h: 125),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 1750; y:4058; w: 26; h: 26),
+ (x: 1900; y:3918; w: 75; h: 75),
+ (x: 2050; y:3918; w: 75; h: 75),
+ (x: 2175; y:4058; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 2500; y:3748; w: 26; h: 26),
+ (x: 2575; y:3548; w: 35; h: 75),
+ (x: 2650; y:3548; w: 35; h: 75),
+ (x: 2700; y:3738; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 2000; y:2173; w: 26; h: 26),
+ (x: 2050; y:2098; w: 50; h: 50),
+ (x: 2100; y:2098; w: 50; h: 50),
+ (x: 2150; y:2198; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 2600; y:2298; w: 50; h: 125),
+ (x: 2750; y:2448; w: 75; h: 75),
+ (x: 2900; y:2573; w: 75; h: 75),
+ (x: 3150; y:2598; w: 75; h: 125),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 2800; y:3198; w: 26; h: 26),
+ (x: 2840; y:2998; w: 50; h: 50),
+ (x: 2880; y:2998; w: 50; h: 50),
+ (x: 2900; y:3198; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 3075; y:4033; w: 26; h: 26),
+ (x: 3325; y:3748; w: 75; h: 125),
+ (x: 3475; y:3748; w: 75; h: 125),
+ (x: 3625; y:4033; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 3200; y:3498; w: 26; h: 26),
+ (x: 3240; y:3398; w: 50; h: 50),
+ (x: 3280; y:3398; w: 50; h: 50),
+ (x: 3300; y:3498; w: 26; h: 26),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 3500; y:3098; w: 50; h: 75),
+ (x: 3650; y:2648; w: 75; h: 125),
+ (x: 3800; y:2648; w: 75; h: 125),
+ (x: 3900; y:3048; w: 50; h: 75),
+ (x: NTPX; y:2048; w: 1; h: 1),
+ (x: 3800; y:2248; w: 50; h: 75),
+ (x: 3875; y:2148; w: 75; h: 75),
+ (x: 3925; y:2098; w: 75; h: 50),
+ (x: 4050; y:2173; w: 50; h: 75),
+ (x: NTPX; y:2048; w: 1; h: 1)
+ );
+ Template43FPoints: array[0..0] of TPoint =
+ (
+ (X: 4095; Y: 0)
+ );
////////////////////////////////////////////////////////////////////////
-var EdgeTemplates: array[0..42] of TEdgeTemplate =
+var EdgeTemplates: array[0..43] of TEdgeTemplate =
(
(BasePoints: @Template0Points;
BasePointsCount: Succ(High(Template0Points));
@@ -2030,8 +2212,8 @@
BasePointsCount: Succ(High(Template41Points));
FillPoints: @Template41FPoints;
FillPointsCount: Succ(High(Template41FPoints));
- BezierizeCount: 3;
- RandPassesCount: 5;
+ BezierizeCount: 2;
+ RandPassesCount: 9;
TemplateHeight: 2048; TemplateWidth: 4096;
canMirror: true; canFlip: true; isNegative: false; canInvert: false;
hasGirders: true;
@@ -2047,19 +2229,30 @@
canMirror: true; canFlip: false; isNegative: false; canInvert: false;
hasGirders: false;
MaxHedgeHogs: 8;
+ ),
+ (BasePoints: @Template43Points;
+ BasePointsCount: Succ(High(Template43Points));
+ FillPoints: @Template43FPoints;
+ FillPointsCount: Succ(High(Template43FPoints));
+ BezierizeCount: 2;
+ RandPassesCount: 9;
+ TemplateHeight: 4096; TemplateWidth: 4096;
+ canMirror: true; canFlip: true; isNegative: false; canInvert: false;
+ hasGirders: true;
+ MaxHedgeHogs: 48;
)
);
const SmallTemplates: array[0..2] of Longword = ( 39, 40, 42 );
const MediumTemplates: array[0..17] of Longword =
( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 );
-const LargeTemplates: array[0..19] of Longword =
+const LargeTemplates: array[0..20] of Longword =
(
18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30, 31, 32, 33, 34, 35, 37, 38
+ 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 43
);
const CavernTemplates: array[0..4] of Longword = (36, 2, 3, 21, 29);
//const WackyTemplates: array[0..4] of Longword = (37, 38, 39, 40, 41);
-const WackyTemplates: array[0..2] of Longword = (37, 38, 41);
+const WackyTemplates: array[0..3] of Longword = (37, 38, 41, 43);
implementation
--- a/hedgewars/uMisc.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uMisc.pas Thu Aug 30 13:02:19 2012 -0400
@@ -28,7 +28,7 @@
procedure movecursor(dx, dy: LongInt);
function doSurfaceConversion(tmpsurf: PSDL_Surface): PSDL_Surface;
-function MakeScreenshot(filename: shortstring): boolean;
+function MakeScreenshot(filename: shortstring; k: LongInt): boolean;
function GetTeamStatString(p: PTeam): shortstring;
{$IFDEF SDL13}
function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline;
@@ -186,19 +186,48 @@
{$ENDIF} // no PNG_SCREENSHOTS
+{$IFDEF USE_VIDEO_RECORDING}
+// make image k times smaller (useful for saving thumbnails)
+procedure ReduceImage(img: PByte; width, height, k: LongInt);
+var i, j, i0, j0, w, h, r, g, b: LongInt;
+begin
+ w:= width div k;
+ h:= height div k;
+
+ // rescale inplace
+ if k <> 1 then
+ begin
+ for i:= 0 to h-1 do
+ for j:= 0 to w-1 do
+ begin
+ r:= 0;
+ g:= 0;
+ b:= 0;
+ for i0:= 0 to k-1 do
+ for j0:= 0 to k-1 do
+ begin
+ r+= img[4*(width*(i*k+i0) + j*k+j0)+0];
+ g+= img[4*(width*(i*k+i0) + j*k+j0)+1];
+ b+= img[4*(width*(i*k+i0) + j*k+j0)+2];
+ end;
+ img[4*(w*i + j)+0]:= r div (k*k);
+ img[4*(w*i + j)+1]:= g div (k*k);
+ img[4*(w*i + j)+2]:= b div (k*k);
+ img[4*(w*i + j)+3]:= 255;
+ end;
+ end;
+end;
+{$ENDIF}
+
// captures and saves the screen. returns true on success.
-function MakeScreenshot(filename: shortstring): Boolean;
+// saved image will be k times smaller than original (useful for saving thumbnails).
+function MakeScreenshot(filename: shortstring; k: LongInt): Boolean;
var p: Pointer;
size: QWord;
image: PScreenshot;
format: GLenum;
ext: string[4];
begin
-// flash
-ScreenFade:= sfFromWhite;
-ScreenFadeValue:= sfMax;
-ScreenFadeSpeed:= 5;
-
{$IFDEF PNG_SCREENSHOTS}
format:= GL_RGBA;
ext:= '.png';
@@ -218,14 +247,18 @@
exit;
end;
-// read pixel from the front buffer
+// read pixels from the front buffer
glReadPixels(0, 0, cScreenWidth, cScreenHeight, format, GL_UNSIGNED_BYTE, p);
+{$IFDEF USE_VIDEO_RECORDING}
+ReduceImage(p, cScreenWidth, cScreenHeight, k);
+{$ENDIF}
+
// allocate and fill structure that will be passed to new thread
New(image); // will be disposed in SaveScreenshot()
-image^.filename:= UserPathPrefix + '/Screenshots/' + filename + ext;
-image^.width:= cScreenWidth;
-image^.height:= cScreenHeight;
+image^.filename:= UserPathPrefix + filename + ext;
+image^.width:= cScreenWidth div k;
+image^.height:= cScreenHeight div k;
image^.size:= size;
image^.buffer:= p;
--- a/hedgewars/uMobile.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uMobile.pas Thu Aug 30 13:02:19 2012 -0400
@@ -28,7 +28,7 @@
interface
function isPhone: Boolean; inline;
-function getScreenDPI: Single; inline;
+function getScreenDPI: Double; inline;
procedure performRumble; inline;
procedure GameLoading; inline;
@@ -36,7 +36,7 @@
procedure SaveLoadingEnded; inline;
implementation
-uses uVariables, uConsole;
+uses uVariables, uConsole, SDLh;
// add here any external call that you need
{$IFDEF IPHONEOS}
@@ -48,10 +48,6 @@
procedure AudioServicesPlaySystemSound(num: LongInt); cdecl; external;
{$ENDIF}
-{$IFDEF ANDROID}
-function Android_JNI_getDensity(): Single; cdecl; external;
-{$ENDIF}
-
// this function is just to determine whether we are running on a limited screen device
function isPhone: Boolean; inline;
begin
@@ -66,10 +62,11 @@
{$ENDIF}
end;
-function getScreenDPI: Single; inline;
+function getScreenDPI: Double; inline;
begin
{$IFDEF ANDROID}
- getScreenDPI:= Android_JNI_getDensity();
+// getScreenDPI:= Android_JNI_getDensity();
+ getScreenDPI:= 1;
{$ELSE}
getScreenDPI:= 1;
{$ENDIF}
--- a/hedgewars/uRandom.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uRandom.pas Thu Aug 30 13:02:19 2012 -0400
@@ -30,9 +30,6 @@
interface
uses uFloat;
-procedure initModule;
-procedure freeModule;
-
procedure SetRandomSeed(Seed: shortstring); // Sets the seed that should be used for generating pseudo-random values.
function GetRandomf: hwFloat; overload; // Returns a pseudo-random hwFloat.
function GetRandom(m: LongWord): LongWord; overload; inline; // Returns a positive pseudo-random integer smaller than m.
@@ -99,15 +96,4 @@
rndSign:= num
end;
-procedure initModule;
-begin
- n:= 54;
- FillChar(cirbuf, 64*sizeof(Longword), 0);
-end;
-
-procedure freeModule;
-begin
-
-end;
-
end.
--- a/hedgewars/uRenderUtils.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uRenderUtils.pas Thu Aug 30 13:02:19 2012 -0400
@@ -92,7 +92,7 @@
textRect.y:= Y;
textRect.w:= w;
textRect.h:= h;
- DrawRoundRect(@finalRect, cWhiteColor, endian(cNearBlackColorChannels.value), Surface, true);
+ DrawRoundRect(@finalRect, cWhiteColor, cNearBlackColor, Surface, true);
clr.r:= (Color shr 16) and $FF;
clr.g:= (Color shr 8) and $FF;
clr.b:= Color and $FF;
--- a/hedgewars/uScript.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uScript.pas Thu Aug 30 13:02:19 2012 -0400
@@ -413,7 +413,7 @@
begin
gear:= GearByUID(lua_tointeger(L, 1));
if gear <> nil then
- DeleteGear(gear);
+ gear^.Message:= gear^.Message or gmDelete;
end;
lc_deletegear:= 0
end;
@@ -745,7 +745,7 @@
TryDo(texsurf <> nil, errmsgCreateSurface, true);
TryDo(SDL_SetColorKey(texsurf, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
- DrawRoundRect(@r, cWhiteColor, cNearBlackColorChannels.value, texsurf, true);
+ DrawRoundRect(@r, cWhiteColor, cNearBlackColor, texsurf, true);
rr:= r;
inc(rr.x, 2); dec(rr.w, 4); inc(rr.y, 2); dec(rr.h, 4);
DrawRoundRect(@rr, clan^.Color, clan^.Color, texsurf, false);
@@ -981,10 +981,13 @@
if (gear <> nil) and (gear^.Kind = gtHedgehog) and (gear^.Hedgehog <> nil) and (CurrentHedgehog <> nil) then
begin
prevgear := CurrentHedgehog^.Gear;
- prevgear^.Active := false;
- prevgear^.State:= prevgear^.State and not gstHHDriven;
- prevgear^.Z := cHHZ;
- prevgear^.Message:= prevgear^.Message or gmRemoveFromList or gmAddToList;
+ if prevgear <> nil then
+ begin
+ prevgear^.Active := false;
+ prevgear^.State:= prevgear^.State and (not gstHHDriven);
+ prevgear^.Z := cHHZ;
+ prevgear^.Message:= prevgear^.Message or gmRemoveFromList or gmAddToList;
+ end;
SwitchCurrentHedgehog(gear^.Hedgehog);
CurrentTeam:= CurrentHedgehog^.Team;
@@ -1808,6 +1811,7 @@
ScriptSetInteger('GameFlags', GameFlags);
ScriptSetString('Seed', cSeed);
ScriptSetInteger('TemplateFilter', cTemplateFilter);
+ScriptSetInteger('TemplateNumber', LuaTemplateNumber);
ScriptSetInteger('MapGen', cMapGen);
ScriptSetInteger('ScreenHeight', cScreenHeight);
ScriptSetInteger('ScreenWidth', cScreenWidth);
@@ -1836,6 +1840,7 @@
// pop game variables
ParseCommand('seed ' + ScriptGetString('Seed'), true);
cTemplateFilter := ScriptGetInteger('TemplateFilter');
+LuaTemplateNumber:= ScriptGetInteger('TemplateNumber');
cMapGen := ScriptGetInteger('MapGen');
GameFlags := ScriptGetInteger('GameFlags');
cHedgehogTurnTime:= ScriptGetInteger('TurnTime');
--- a/hedgewars/uSound.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uSound.pas Thu Aug 30 13:02:19 2012 -0400
@@ -587,11 +587,8 @@
RegisterVariable('mute' , @chMute , true );
MusicFN:='';
- isMusicEnabled:= true;
- isSoundEnabled:= true;
isAudioMuted:= false;
isSEBackup:= isSoundEnabled;
- cInitVolume:= 100;
Volume:= 0;
defVoicepack:= AskForVoicepack('Default');
@@ -615,6 +612,11 @@
begin
if isSoundEnabled then
ReleaseSound(true);
+ // koda still needs to fix this properly. when he rearranged things, he made these variables get
+ // reset after argparsers picks them up
+ isMusicEnabled:= true;
+ isSoundEnabled:= true;
+ cInitVolume:= 100;
end;
end.
--- a/hedgewars/uStore.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uStore.pas Thu Aug 30 13:02:19 2012 -0400
@@ -40,13 +40,17 @@
procedure ShowWeaponTooltip(x, y: LongInt);
procedure FreeWeaponTooltip;
procedure MakeCrossHairs;
+{$IFDEF USE_VIDEO_RECORDING}
+procedure InitOffscreenOpenGL;
+{$ENDIF}
procedure WarpMouse(x, y: Word); inline;
procedure SwapBuffers; inline;
implementation
uses uMisc, uConsole, uMobile, uVariables, uUtils, uTextures, uRender, uRenderUtils, uCommands,
- uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF};
+ uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF}
+ {$IF NOT DEFINED(SDL13) AND DEFINED(USE_VIDEO_RECORDING)}, glut {$ENDIF};
//type TGPUVendor = (gvUnknown, gvNVIDIA, gvATI, gvIntel, gvApple);
@@ -131,10 +135,11 @@
procedure WriteNames(Font: THWFont);
var t: LongInt;
- i: LongInt;
+ i, maxLevel: LongInt;
r, rr: TSDL_Rect;
drY: LongInt;
texsurf, flagsurf, iconsurf: PSDL_Surface;
+ foundBot: boolean;
begin
r.x:= 0;
r.y:= 0;
@@ -151,7 +156,7 @@
TryDo(texsurf <> nil, errmsgCreateSurface, true);
TryDo(SDL_SetColorKey(texsurf, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
- DrawRoundRect(@r, cWhiteColor, cNearBlackColorChannels.value, texsurf, true);
+ DrawRoundRect(@r, cWhiteColor, cNearBlackColor, texsurf, true);
rr:= r;
inc(rr.x, 2); dec(rr.w, 4); inc(rr.y, 2); dec(rr.h, 4);
DrawRoundRect(@rr, Clan^.Color, Clan^.Color, texsurf, false);
@@ -172,11 +177,28 @@
DrawRoundRect(@r, cWhiteColor, cNearBlackColor, texsurf, true);
// overwrite flag for cpu teams and keep players from using it
- if (Hedgehogs[0].Gear <> nil) and (Hedgehogs[0].BotLevel > 0) then
- if Flag = 'hedgewars' then
- Flag:= 'cpu'
- else if Flag = 'cpu' then
- Flag:= 'hedgewars';
+ foundBot:= false;
+ maxLevel:= -1;
+ for i:= 0 to cMaxHHIndex do
+ with Hedgehogs[i] do
+ if (Gear <> nil) and (BotLevel > 0) then
+ begin
+ foundBot:= true;
+ // initially was going to do the highest botlevel of the team, but for now, just apply if entire team has same bot level
+ if maxLevel = -1 then maxLevel:= BotLevel
+ else if (maxLevel > 0) and (maxLevel <> BotLevel) then maxLevel:= 0;
+ //if (maxLevel > 0) and (BotLevel < maxLevel) then maxLevel:= BotLevel
+ end
+ else if Gear <> nil then maxLevel:= 0;
+
+ if foundBot then
+ begin
+ // disabled the plain flag - I think it looks ok even w/ full bars obscuring CPU
+ //if (maxLevel > 0) and (maxLevel < 3) then Flag:= 'cpu_plain' else
+ Flag:= 'cpu'
+ end
+ else if (Flag = 'cpu') or (Flag = 'cpu_plain') then
+ Flag:= 'hedgewars';
flagsurf:= LoadImage(UserPathz[ptFlags] + '/' + Flag, ifNone);
if flagsurf = nil then
@@ -186,16 +208,27 @@
if flagsurf = nil then
flagsurf:= LoadImage(Pathz[ptFlags] + '/hedgewars', ifNone);
TryDo(flagsurf <> nil, 'Failed to load flag "' + Flag + '" as well as the default flag', true);
+
+ case maxLevel of
+ 1: copyToXY(SpritesData[sprBotlevels].Surface, flagsurf, 0, 0);
+ 2: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 5, 2, 17, 13, 5, 2);
+ 3: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 9, 5, 13, 10, 9, 5);
+ 4: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 13, 9, 9, 6, 13, 9);
+ 5: copyToXYFromRect(SpritesData[sprBotlevels].Surface, flagsurf, 17, 11, 5, 4, 17, 11)
+ end;
+
copyToXY(flagsurf, texsurf, 2, 2);
SDL_FreeSurface(flagsurf);
flagsurf:= nil;
+
// restore black border pixels inside the flag
PLongwordArray(texsurf^.pixels)^[32 * 2 + 2]:= cNearBlackColor;
PLongwordArray(texsurf^.pixels)^[32 * 2 + 23]:= cNearBlackColor;
PLongwordArray(texsurf^.pixels)^[32 * 16 + 2]:= cNearBlackColor;
PLongwordArray(texsurf^.pixels)^[32 * 16 + 23]:= cNearBlackColor;
+
FlagTex:= Surface2Tex(texsurf, false);
SDL_FreeSurface(texsurf);
texsurf:= nil;
@@ -204,7 +237,7 @@
dec(drY, r.h + 2);
DrawHealthY:= drY;
- for i:= 0 to 7 do
+ for i:= 0 to cMaxHHIndex do
with Hedgehogs[i] do
if Gear <> nil then
begin
@@ -294,7 +327,6 @@
WriteLnToConsole(msgOK)
end;
-WriteNames(fnt16);
MakeCrossHairs;
LoadGraves;
if not reload then
@@ -391,6 +423,8 @@
Surface:= nil
end;
+WriteNames(fnt16);
+
if not reload then
AddProgress;
@@ -438,6 +472,31 @@
IMG_Quit();
end;
+{$IF NOT DEFINED(S3D_DISABLED) OR DEFINED(USE_VIDEO_RECORDING)}
+procedure CreateFramebuffer(var frame, depth, tex: GLuint);
+begin
+ glGenFramebuffersEXT(1, @frame);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frame);
+ glGenRenderbuffersEXT(1, @depth);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth);
+ glGenTextures(1, @tex);
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);
+end;
+
+procedure DeleteFramebuffer(var frame, depth, tex: GLuint);
+begin
+ glDeleteTextures(1, @tex);
+ glDeleteRenderbuffersEXT(1, @depth);
+ glDeleteFramebuffersEXT(1, @frame);
+end;
+{$ENDIF}
+
procedure StoreRelease(reload: boolean);
var ii: TSprite;
ai: TAmmoType;
@@ -511,15 +570,15 @@
end;
end;
end;
+{$IFDEF USE_VIDEO_RECORDING}
+ if defaultFrame <> 0 then
+ DeleteFramebuffer(defaultFrame, depthv, texv);
+{$ENDIF}
{$IFNDEF S3D_DISABLED}
if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then
begin
- glDeleteTextures(1, @texl);
- glDeleteRenderbuffersEXT(1, @depthl);
- glDeleteFramebuffersEXT(1, @framel);
- glDeleteTextures(1, @texr);
- glDeleteRenderbuffersEXT(1, @depthr);
- glDeleteFramebuffersEXT(1, @framer)
+ DeleteFramebuffer(framel, depthl, texl);
+ DeleteFramebuffer(framer, depthr, texr);
end
{$ENDIF}
end;
@@ -628,6 +687,7 @@
procedure SetupOpenGL;
//var vendor: shortstring = '';
var buf: array[byte] of char;
+ AuxBufNum: LongInt;
begin
buf[0]:= char(0); // avoid compiler hint
AddFileLog('Setting up OpenGL (using driver: ' + shortstring(SDL_VideoDriverName(buf, sizeof(buf))) + ')');
@@ -673,14 +733,43 @@
{$ENDIF}
//SupportNPOTT:= glLoadExtension('GL_ARB_texture_non_power_of_two');
*)
+ glGetIntegerv(GL_AUX_BUFFERS, @AuxBufNum);
// everyone love debugging
AddFileLog('OpenGL-- Renderer: ' + shortstring(pchar(glGetString(GL_RENDERER))));
AddFileLog(' |----- Vendor: ' + shortstring(pchar(glGetString(GL_VENDOR))));
AddFileLog(' |----- Version: ' + shortstring(pchar(glGetString(GL_VERSION))));
AddFileLog(' |----- Texture Size: ' + inttostr(MaxTextureSize));
- AddFileLog(' \----- Extensions: ' + shortstring(pchar(glGetString(GL_EXTENSIONS))));
- //TODO: don't have the Extensions line trimmed but slipt it into multiple lines
+ AddFileLog(' |----- Number of auxilary buffers: ' + inttostr(AuxBufNum));
+ AddFileLog(' \----- Extensions: ');
+ AddFileLogRaw(glGetString(GL_EXTENSIONS));
+ AddFileLog('');
+ //TODO: slipt Extensions line into multiple lines
+
+ defaultFrame:= 0;
+{$IFDEF USE_VIDEO_RECORDING}
+ if GameType = gmtRecord then
+ begin
+ if glLoadExtension('GL_EXT_framebuffer_object') then
+ begin
+ CreateFramebuffer(defaultFrame, depthv, texv);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame);
+ AddFileLog('Using framebuffer for video recording.');
+ end
+ else if AuxBufNum > 0 then
+ begin
+ glDrawBuffer(GL_AUX0);
+ glReadBuffer(GL_AUX0);
+ AddFileLog('Using auxilary buffer for video recording.');
+ end
+ else
+ begin
+ glDrawBuffer(GL_BACK);
+ glReadBuffer(GL_BACK);
+ AddFileLog('Warning: off-screen rendering is not supported; using back buffer but it may not work.');
+ end;
+ end;
+{$ENDIF}
{$IFNDEF S3D_DISABLED}
if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then
@@ -688,36 +777,11 @@
// prepare left and right frame buffers and associated textures
if glLoadExtension('GL_EXT_framebuffer_object') then
begin
- // left
- glGenFramebuffersEXT(1, @framel);
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framel);
- glGenRenderbuffersEXT(1, @depthl);
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthl);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
- glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthl);
- glGenTextures(1, @texl);
- glBindTexture(GL_TEXTURE_2D, texl);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texl, 0);
-
- // right
- glGenFramebuffersEXT(1, @framer);
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framer);
- glGenRenderbuffersEXT(1, @depthr);
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthr);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, cScreenWidth, cScreenHeight);
- glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthr);
- glGenTextures(1, @texr);
- glBindTexture(GL_TEXTURE_2D, texr);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cScreenWidth, cScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texr, 0);
+ CreateFramebuffer(framel, depthl, texl);
+ CreateFramebuffer(framer, depthr, texr);
// reset
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame)
end
else
cStereoMode:= smNone;
@@ -991,6 +1055,34 @@
WeaponTooltipTex:= nil
end;
+{$IFDEF USE_VIDEO_RECORDING}
+{$IFDEF SDL13}
+procedure InitOffscreenOpenGL;
+begin
+ // create hidden window
+ SDLwindow:= SDL_CreateWindow('hedgewars (you don''t see this)',
+ SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK,
+ cScreenWidth, cScreenHeight,
+ SDL_WINDOW_HIDDEN or SDL_WINDOW_OPENGL);
+ SDLTry(SDLwindow <> nil, true);
+ SetupOpenGL();
+end;
+{$ELSE}
+procedure InitOffscreenOpenGL;
+var ArgCount: LongInt;
+ PrgName: pchar;
+begin
+ ArgCount:= 1;
+ PrgName:= 'hwengine';
+ glutInit(@ArgCount, @PrgName);
+ glutInitWindowSize(cScreenWidth, cScreenHeight);
+ glutCreateWindow('hedgewars (you don''t see this)'); // we don't need a window, but if this function is not called then OpenGL will not be initialized
+ glutHideWindow();
+ SetupOpenGL();
+end;
+{$ENDIF} // SDL13
+{$ENDIF} // USE_VIDEO_RECORDING
+
procedure chFullScr(var s: shortstring);
var flags: Longword = 0;
reinit: boolean = false;
@@ -1171,6 +1263,8 @@
procedure SwapBuffers; inline;
begin
+ if GameType = gmtRecord then
+ exit;
{$IFDEF SDL13}
SDL_GL_SwapWindow(SDLwindow);
{$ELSE}
--- a/hedgewars/uTeams.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uTeams.pas Thu Aug 30 13:02:19 2012 -0400
@@ -473,7 +473,7 @@
begin
Gear^.Invulnerable:= false;
Gear^.Damage:= Gear^.Health;
- Gear^.State:= (Gear^.State or gstHHGone) and not gstHHDriven
+ Gear^.State:= (Gear^.State or gstHHGone) and (not gstHHDriven)
end
end
end;
@@ -531,7 +531,7 @@
AddTeam(Color);
CurrentTeam^.TeamName:= ts;
CurrentTeam^.PlayerHash:= s;
- if GameType in [gmtDemo, gmtSave] then
+ if GameType in [gmtDemo, gmtSave, gmtRecord] then
CurrentTeam^.ExtDriven:= true;
CurrentTeam^.voicepack:= AskForVoicepack('Default')
--- a/hedgewars/uTypes.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uTypes.pas Thu Aug 30 13:02:19 2012 -0400
@@ -39,7 +39,7 @@
TGameState = (gsLandGen, gsStart, gsGame, gsChat, gsConfirm, gsExit, gsSuspend);
// Game types that help determining what the engine is actually supposed to do
- TGameType = (gmtLocal, gmtDemo, gmtNet, gmtSave, gmtLandPreview, gmtSyntax);
+ TGameType = (gmtLocal, gmtDemo, gmtNet, gmtSave, gmtLandPreview, gmtSyntax, gmtRecord);
// Different files are stored in different folders, this enumeration is used to tell which folder to use
TPathType = (ptNone, ptData, ptGraphics, ptThemes, ptCurrTheme, ptTeams, ptMaps,
@@ -86,7 +86,7 @@
sprHandResurrector, sprCross, sprAirDrill, sprNapalmBomb,
sprBulletHit, sprSnowball, sprHandSnowball, sprSnow,
sprSDFlake, sprSDWater, sprSDCloud, sprSDSplash, sprSDDroplet, sprTardis,
- sprSlider
+ sprSlider, sprBotlevels
);
// Gears that interact with other Gears and/or Land
@@ -235,6 +235,7 @@
Kind: TGearType;
Pos: Longword;
doStep: TGearStepProcedure;
+ stepFreq: Longword;
Radius: LongInt;
Angle, Power : Longword;
DirAngle: real;
--- a/hedgewars/uUtils.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uUtils.pas Thu Aug 30 13:02:19 2012 -0400
@@ -43,7 +43,7 @@
function StrToInt(s: shortstring): LongInt;
function FloatToStr(n: hwFloat): shortstring;
-function DxDy2Angle(const _dY, _dX: hwFloat): GLfloat;
+function DxDy2Angle(const _dY, _dX: hwFloat): GLfloat; inline;
function DxDy2Angle32(const _dY, _dX: hwFloat): LongInt;
function DxDy2AttackAngle(const _dY, _dX: hwFloat): LongInt;
function DxDy2AttackAnglef(const _dY, _dX: extended): LongInt;
@@ -61,6 +61,7 @@
function CheckCJKFont(s: ansistring; font: THWFont): THWFont;
procedure AddFileLog(s: shortstring);
+procedure AddFileLogRaw(s: pchar); cdecl;
function CheckNoTeamOrHH: boolean; inline;
@@ -81,6 +82,9 @@
{$IFDEF DEBUGFILE}
var f: textfile;
+{$IFDEF USE_VIDEO_RECORDING}
+ logMutex: TRTLCriticalSection; // mutex for debug file
+{$ENDIF}
{$ENDIF}
var CharArray: array[byte] of Char;
@@ -182,15 +186,11 @@
end;
-function DxDy2Angle(const _dY, _dX: hwFloat): GLfloat;
+function DxDy2Angle(const _dY, _dX: hwFloat): GLfloat; inline;
var dY, dX: Extended;
begin
-dY:= _dY.QWordValue / $100000000;
-if _dY.isNegative then
- dY:= - dY;
-dX:= _dX.QWordValue / $100000000;
-if _dX.isNegative then
- dX:= - dX;
+dY:= hwFloat2Float(_dY);
+dX:= hwFloat2Float(_dX);
DxDy2Angle:= arctan2(dY, dX) * 180 / pi
end;
@@ -198,12 +198,8 @@
const _16divPI: Extended = 16/pi;
var dY, dX: Extended;
begin
-dY:= _dY.QWordValue / $100000000;
-if _dY.isNegative then
- dY:= - dY;
-dX:= _dX.QWordValue / $100000000;
-if _dX.isNegative then
- dX:= - dX;
+dY:= hwFloat2Float(_dY);
+dX:= hwFloat2Float(_dX);
DxDy2Angle32:= trunc(arctan2(dY, dX) * _16divPI) and $1f
end;
@@ -211,12 +207,8 @@
const MaxAngleDivPI: Extended = cMaxAngle/pi;
var dY, dX: Extended;
begin
-dY:= _dY.QWordValue / $100000000;
-if _dY.isNegative then
- dY:= - dY;
-dX:= _dX.QWordValue / $100000000;
-if _dX.isNegative then
- dX:= - dX;
+dY:= hwFloat2Float(_dY);
+dX:= hwFloat2Float(_dX);
DxDy2AttackAngle:= trunc(arctan2(dY, dX) * MaxAngleDivPI)
end;
@@ -303,11 +295,31 @@
begin
s:= s;
{$IFDEF DEBUGFILE}
+{$IFDEF USE_VIDEO_RECORDING}
+EnterCriticalSection(logMutex);
+{$ENDIF}
writeln(f, inttostr(GameTicks) + ': ' + s);
-flush(f)
+flush(f);
+{$IFDEF USE_VIDEO_RECORDING}
+LeaveCriticalSection(logMutex);
+{$ENDIF}
{$ENDIF}
end;
+procedure AddFileLogRaw(s: pchar); cdecl;
+begin
+s:= s;
+{$IFDEF DEBUGFILE}
+{$IFDEF USE_VIDEO_RECORDING}
+EnterCriticalSection(logMutex);
+{$ENDIF}
+write(f, s);
+flush(f);
+{$IFDEF USE_VIDEO_RECORDING}
+LeaveCriticalSection(logMutex);
+{$ENDIF}
+{$ENDIF}
+end;
function CheckCJKFont(s: ansistring; font: THWFont): THWFont;
var l, i : LongInt;
@@ -397,9 +409,17 @@
begin
{$IFDEF DEBUGFILE}
if isGame then
- logfileBase:= 'game'
+ begin
+ if GameType = gmtRecord then
+ logfileBase:= 'rec'
+ else
+ logfileBase:= 'game';
+ end
else
logfileBase:= 'preview';
+{$IFDEF USE_VIDEO_RECORDING}
+ InitCriticalSection(logMutex);
+{$ENDIF}
{$I-}
{$IFDEF MOBILE}
{$IFDEF IPHONEOS} Assign(f,'../Documents/hw-' + logfileBase + '.log'); {$ENDIF}
@@ -436,6 +456,9 @@
writeln(f, 'halt at ' + inttostr(GameTicks) + ' ticks. TurnTimeLeft = ' + inttostr(TurnTimeLeft));
flush(f);
close(f);
+{$IFDEF USE_VIDEO_RECORDING}
+ DoneCriticalSection(logMutex);
+{$ENDIF}
{$ENDIF}
end;
--- a/hedgewars/uVariables.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uVariables.pas Thu Aug 30 13:02:19 2012 -0400
@@ -52,6 +52,15 @@
cReadyDelay : Longword = 5000;
cStereoMode : TStereoMode = smNone;
cOnlyStats : boolean = False;
+{$IFDEF USE_VIDEO_RECORDING}
+ RecPrefix : shortstring;
+ cAVFormat : shortstring;
+ cVideoCodec : shortstring;
+ cVideoFramerateNum : LongInt;
+ cVideoFramerateDen : LongInt;
+ cVideoQuality : LongInt;
+ cAudioCodec : shortstring;
+{$ENDIF}
//////////////////////////
cMapName : shortstring = '';
@@ -60,8 +69,10 @@
isPaused : boolean;
isInMultiShoot : boolean;
isSpeed : boolean;
+ SpeedStart : LongWord;
fastUntilLag : boolean;
+ fastScrolling : boolean;
autoCameraOn : boolean;
CheckSum : LongWord;
@@ -185,6 +196,8 @@
hiddenHedgehogs : array [0..cMaxHHs] of PHedgehog;
hiddenHedgehogsNumber : longint;
+ LuaTemplateNumber : LongWord;
+
VoiceList : array[0..7] of TVoice = (
( snd: sndNone; voicepack: nil),
( snd: sndNone; voicepack: nil),
@@ -646,7 +659,9 @@
(FileName: 'TARDIS'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
Width: 48; Height: 79; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprTardis
(FileName: 'slider'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
- Width: 3; Height: 17; imageWidth: 3; imageHeight: 17; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: false) // sprSlider
+ Width: 3; Height: 17; imageWidth: 3; imageHeight: 17; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: false), // sprSlider
+ (FileName: 'botlevels'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
+ Width: 22; Height: 15; imageWidth: 22; imageHeight: 15; saveSurf: true; priority: tpLow; getDimensions: false; getImageDimensions: false) // sprBotlevels
);
const
@@ -1448,7 +1463,8 @@
NumberInCase: 1;
Ammo: (Propz: ammoprop_ForwMsgs or
ammoprop_NoCrosshair or
- ammoprop_DontHold;
+ ammoprop_DontHold or
+ ammoprop_Track;
Count: 1;
NumPerTurn: 0;
Timer: 0;
@@ -2446,6 +2462,10 @@
framel, framer, depthl, depthr: GLuint;
texl, texr: GLuint;
+ // video recorder framebuffer and texture
+ defaultFrame, depthv: GLuint;
+ texv: GLuint;
+
VisualGearLayers: array[0..6] of PVisualGear;
lastVisualGearByUID: PVisualGear;
vobFrameTicks, vobFramesCount, vobCount: Longword;
@@ -2571,7 +2591,7 @@
cExplosives := 2;
GameState := Low(TGameState);
- GameType := gmtLocal;
+// GameType := gmtLocal;
zoom := cDefaultZoomLevel;
ZoomValue := cDefaultZoomLevel;
WeaponTooltipTex:= nil;
@@ -2586,7 +2606,9 @@
isPaused := false;
isInMultiShoot := false;
isSpeed := false;
+ SpeedStart := 0;
fastUntilLag := false;
+ fastScrolling := false;
autoCameraOn := true;
cScriptName := '';
cSeed := '';
@@ -2621,6 +2643,8 @@
SDWaterOpacity:= $80;
LuaGoals:= '';
+
+ LuaTemplateNumber:= 0;
end;
procedure freeModule;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uVideoRec.pas Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,369 @@
+(*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *)
+
+
+{$INCLUDE "options.inc"}
+
+unit uVideoRec;
+
+{$IFNDEF USE_VIDEO_RECORDING}
+interface
+implementation
+end.
+{$ELSE}
+
+{$IFNDEF WIN32}
+ {$LINKLIB ../bin/libavwrapper.a}
+{$ENDIF}
+
+interface
+
+var flagPrerecording: boolean = false;
+
+function BeginVideoRecording: Boolean;
+function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean;
+procedure EncodeFrame;
+procedure StopVideoRecording;
+
+procedure BeginPreRecording;
+procedure StopPreRecording;
+procedure SaveCameraPosition;
+
+procedure freeModule;
+
+implementation
+
+uses uVariables, uUtils, GLunit, SDLh, SysUtils, uIO, uMisc, uTypes;
+
+type TAddFileLogRaw = procedure (s: pchar); cdecl;
+
+procedure AVWrapper_Init(
+ AddLog: TAddFileLogRaw;
+ filename, desc, soundFile, format, vcodec, acodec: PChar;
+ width, height, framerateNum, framerateDen, vquality: LongInt); cdecl; external {$IFDEF WIN32}'libavwrapper.dll'{$ENDIF};
+procedure AVWrapper_Close; cdecl; external {$IFDEF WIN32}'libavwrapper.dll'{$ENDIF};
+procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external {$IFDEF WIN32}'libavwrapper.dll'{$ENDIF};
+
+type TFrame = record
+ realTicks: LongWord;
+ gameTicks: LongWord;
+ CamX, CamY: LongInt;
+ zoom: single;
+ end;
+
+var YCbCr_Planes: array[0..2] of PByte;
+ RGB_Buffer: PByte;
+ cameraFile: File of TFrame;
+ audioFile: File;
+ numPixels: LongWord;
+ startTime, numFrames, curTime, progress, maxProgress: LongWord;
+ soundFilePath: shortstring;
+ thumbnailSaved : Boolean;
+
+function BeginVideoRecording: Boolean;
+var filename, desc: shortstring;
+begin
+ AddFileLog('BeginVideoRecording');
+
+{$IOCHECKS OFF}
+ // open file with prerecorded camera positions
+ filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin';
+ Assign(cameraFile, filename);
+ Reset(cameraFile);
+ maxProgress:= FileSize(cameraFile);
+ if IOResult <> 0 then
+ begin
+ AddFileLog('Error: Could not read from ' + filename);
+ exit(false);
+ end;
+{$IOCHECKS ON}
+
+ // store some description in output file
+ desc:= '';
+ if UserNick <> '' then
+ desc+= 'Player: ' + UserNick + #10;
+ if recordFileName <> '' then
+ desc+= 'Record: ' + recordFileName + #10;
+ if cMapName <> '' then
+ desc+= 'Map: ' + cMapName + #10;
+ if Theme <> '' then
+ desc+= 'Theme: ' + Theme + #10;
+ desc+= 'prefix[' + RecPrefix + ']prefix';
+ desc+= #0;
+
+ filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + #0;
+ soundFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw' + #0;
+ cAVFormat+= #0;
+ cAudioCodec+= #0;
+ cVideoCodec+= #0;
+ AVWrapper_Init(@AddFileLogRaw, @filename[1], @desc[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1],
+ cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cVideoQuality);
+
+ numPixels:= cScreenWidth*cScreenHeight;
+ YCbCr_Planes[0]:= GetMem(numPixels);
+ YCbCr_Planes[1]:= GetMem(numPixels div 4);
+ YCbCr_Planes[2]:= GetMem(numPixels div 4);
+
+ if (YCbCr_Planes[0] = nil) or (YCbCr_Planes[1] = nil) or (YCbCr_Planes[2] = nil) then
+ begin
+ AddFileLog('Error: Could not allocate memory for video recording (YCbCr buffer).');
+ exit(false);
+ end;
+
+ RGB_Buffer:= GetMem(4*numPixels);
+ if RGB_Buffer = nil then
+ begin
+ AddFileLog('Error: Could not allocate memory for video recording (RGB buffer).');
+ exit(false);
+ end;
+
+ curTime:= 0;
+ numFrames:= 0;
+ progress:= 0;
+ BeginVideoRecording:= true;
+end;
+
+procedure StopVideoRecording;
+begin
+ AddFileLog('StopVideoRecording');
+ FreeMem(YCbCr_Planes[0], numPixels);
+ FreeMem(YCbCr_Planes[1], numPixels div 4);
+ FreeMem(YCbCr_Planes[2], numPixels div 4);
+ FreeMem(RGB_Buffer, 4*numPixels);
+ Close(cameraFile);
+ AVWrapper_Close();
+ Erase(cameraFile);
+ DeleteFile(soundFilePath);
+ SendIPC(_S'v'); // inform frontend that we finished
+end;
+
+function pixel(x, y, color: LongInt): LongInt;
+begin
+ pixel:= RGB_Buffer[(cScreenHeight-y-1)*cScreenWidth*4 + x*4 + color];
+end;
+
+procedure EncodeFrame;
+var x, y, r, g, b: LongInt;
+ s: shortstring;
+begin
+ // read pixels from OpenGL
+ glReadPixels(0, 0, cScreenWidth, cScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, RGB_Buffer);
+
+ // convert to YCbCr 4:2:0 format
+ // Y
+ for y := 0 to cScreenHeight-1 do
+ for x := 0 to cScreenWidth-1 do
+ YCbCr_Planes[0][y*cScreenWidth + x]:= Byte(16 + ((16828*pixel(x,y,0) + 33038*pixel(x,y,1) + 6416*pixel(x,y,2)) shr 16));
+
+ // Cb and Cr
+ for y := 0 to cScreenHeight div 2 - 1 do
+ for x := 0 to cScreenWidth div 2 - 1 do
+ begin
+ r:= pixel(2*x,2*y,0) + pixel(2*x+1,2*y,0) + pixel(2*x,2*y+1,0) + pixel(2*x+1,2*y+1,0);
+ g:= pixel(2*x,2*y,1) + pixel(2*x+1,2*y,1) + pixel(2*x,2*y+1,1) + pixel(2*x+1,2*y+1,1);
+ b:= pixel(2*x,2*y,2) + pixel(2*x+1,2*y,2) + pixel(2*x,2*y+1,2) + pixel(2*x+1,2*y+1,2);
+ YCbCr_Planes[1][y*(cScreenWidth div 2) + x]:= Byte(128 + ((-2428*r - 4768*g + 7196*b) shr 16));
+ YCbCr_Planes[2][y*(cScreenWidth div 2) + x]:= Byte(128 + (( 7196*r - 6026*g - 1170*b) shr 16));
+ end;
+
+ AVWrapper_WriteFrame(YCbCr_Planes[0], YCbCr_Planes[1], YCbCr_Planes[2]);
+
+ // inform frontend that we have encoded new frame
+ s[0]:= #3;
+ s[1]:= 'p'; // p for progress
+ SDLNet_Write16(progress*10000 div maxProgress, @s[2]);
+ SendIPC(s);
+ inc(numFrames);
+end;
+
+function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean;
+var frame: TFrame;
+begin
+ // we need to skip or duplicate frames to match target framerate
+ while Int64(curTime)*cVideoFramerateNum <= Int64(numFrames)*cVideoFramerateDen*1000 do
+ begin
+ {$IOCHECKS OFF}
+ if eof(cameraFile) then
+ exit(false);
+ BlockRead(cameraFile, frame, 1);
+ {$IOCHECKS ON}
+ curTime:= frame.realTicks;
+ WorldDx:= frame.CamX;
+ WorldDy:= frame.CamY + cScreenHeight div 2;
+ zoom:= frame.zoom*cScreenWidth;
+ ZoomValue:= zoom;
+ inc(progress);
+ newRealTicks:= frame.realTicks;
+ newGameTicks:= frame.gameTicks;
+ end;
+ LoadNextCameraPosition:= true;
+end;
+
+// Callback which records sound.
+// This procedure may be called from different thread.
+procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl;
+begin
+ udata:= udata; // avoid warning
+{$IOCHECKS OFF}
+ BlockWrite(audioFile, stream^, len);
+{$IOCHECKS ON}
+end;
+
+procedure SaveThumbnail;
+var thumbpath: shortstring;
+ k: LongInt;
+begin
+ thumbpath:= '/VideoTemp/' + RecPrefix;
+ AddFileLog('Saving thumbnail ' + thumbpath);
+ k:= max(max(cScreenWidth, cScreenHeight) div 400, 1); // here 400 is minimum size of thumbnail
+ MakeScreenshot(thumbpath, k);
+ thumbnailSaved:= true;
+end;
+
+// copy file (free pascal doesn't have copy file function)
+procedure CopyFile(src, dest: shortstring);
+var inF, outF: file;
+ buffer: array[0..1023] of byte;
+ result: LongInt;
+begin
+{$IOCHECKS OFF}
+ result:= 0; // avoid compiler hint
+
+ Assign(inF, src);
+ Reset(inF, 1);
+ if IOResult <> 0 then
+ begin
+ AddFileLog('Error: Could not read from ' + src);
+ exit;
+ end;
+
+ Assign(outF, dest);
+ Rewrite(outF, 1);
+ if IOResult <> 0 then
+ begin
+ AddFileLog('Error: Could not write to ' + dest);
+ exit;
+ end;
+
+ repeat
+ BlockRead(inF, buffer, 1024, result);
+ BlockWrite(outF, buffer, result);
+ until result < 1024;
+{$IOCHECKS ON}
+end;
+
+procedure BeginPreRecording;
+var format: word;
+ filename: shortstring;
+ frequency, channels: LongInt;
+begin
+ AddFileLog('BeginPreRecording');
+
+ thumbnailSaved:= false;
+ RecPrefix:= 'hw-' + FormatDateTime('YYYY-MM-DD_HH-mm-ss-z', Now());
+
+ // If this video is recorded from demo executed directly (without frontend)
+ // then we need to copy demo so that frontend will be able to find it later.
+ if recordFileName <> '' then
+ begin
+ if GameType <> gmtDemo then // this is save and game demo is not recording, abort
+ exit;
+ CopyFile(recordFileName, UserPathPrefix + '/VideoTemp/' + RecPrefix + '.hwd');
+ end;
+
+ Mix_QuerySpec(@frequency, @format, @channels);
+ AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
+ if format <> $8010 then
+ begin
+ // TODO: support any audio format
+ AddFileLog('Error: Unexpected audio format ' + IntToStr(format));
+ exit;
+ end;
+
+{$IOCHECKS OFF}
+ // create sound file
+ filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw';
+ Assign(audioFile, filename);
+ Rewrite(audioFile, 1);
+ if IOResult <> 0 then
+ begin
+ AddFileLog('Error: Could not write to ' + filename);
+ exit;
+ end;
+
+ // create file with camera positions
+ filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtout';
+ Assign(cameraFile, filename);
+ Rewrite(cameraFile);
+ if IOResult <> 0 then
+ begin
+ AddFileLog('Error: Could not write to ' + filename);
+ exit;
+ end;
+
+ // save audio parameters in sound file
+ BlockWrite(audioFile, frequency, 4);
+ BlockWrite(audioFile, channels, 4);
+{$IOCHECKS ON}
+
+ // register callback for actual audio recording
+ Mix_SetPostMix(@RecordPostMix, nil);
+
+ startTime:= SDL_GetTicks();
+ flagPrerecording:= true;
+end;
+
+procedure StopPreRecording;
+begin
+ AddFileLog('StopPreRecording');
+ flagPrerecording:= false;
+
+ // call SDL_LockAudio because RecordPostMix may be executing right now
+ SDL_LockAudio();
+ Close(audioFile);
+ Close(cameraFile);
+ Mix_SetPostMix(nil, nil);
+ SDL_UnlockAudio();
+
+ if not thumbnailSaved then
+ SaveThumbnail();
+end;
+
+procedure SaveCameraPosition;
+var frame: TFrame;
+begin
+ if (not thumbnailSaved) and (ScreenFade = sfNone) then
+ SaveThumbnail();
+
+ frame.realTicks:= SDL_GetTicks() - startTime;
+ frame.gameTicks:= GameTicks;
+ frame.CamX:= WorldDx;
+ frame.CamY:= WorldDy - cScreenHeight div 2;
+ frame.zoom:= zoom/cScreenWidth;
+ BlockWrite(cameraFile, frame, 1);
+end;
+
+procedure freeModule;
+begin
+ if flagPrerecording then
+ StopPreRecording();
+end;
+
+end.
+
+{$ENDIF} // USE_VIDEO_RECORDING
--- a/hedgewars/uVisualGears.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uVisualGears.pas Thu Aug 30 13:02:19 2012 -0400
@@ -135,7 +135,7 @@
sp: real;
begin
AddVisualGear:= nil;
-if ((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet))) and // we are scrolling now
+if ((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) or fastScrolling) and // we are scrolling now
((Kind <> vgtCloud) and (not Critical)) then
exit;
@@ -284,8 +284,9 @@
begin
dx:= 0.005 * (random(15) + 10);
dy:= 0.001 * (random(40) + 20);
- if random(2) = 0 then
- dx := -dx;
+ if random(2) = 0 then dx := -dx;
+ if random(2) = 0 then Tag:= 1
+ else Tag:= -1;
Frame:= 7 - random(2);
FrameTicks:= random(20) + 15;
end;
@@ -295,6 +296,8 @@
dy:= 0;
FrameTicks:= 740;
Frame:= 19;
+ Scale:= 0.75;
+ Timer:= 1;
end;
vgtDroplet:
begin
@@ -640,9 +643,9 @@
vgtSmoke: DrawTextureF(SpritesData[sprSmoke].Texture, Gear^.scale, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, 7 - Gear^.Frame, 1, SpritesData[sprSmoke].Width, SpritesData[sprSmoke].Height);
vgtSmokeWhite: DrawSprite(sprSmokeWhite, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame);
vgtDust: if Gear^.State = 1 then
- DrawSpriteRotatedF(sprSnowDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame, 1, Gear^.Angle)
+ DrawSpriteRotatedF(sprSnowDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame, Gear^.Tag, Gear^.Angle)
else
- DrawSprite(sprDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame);
+ DrawSpriteRotatedF(sprDust, round(Gear^.X) + WorldDx - 11, round(Gear^.Y) + WorldDy - 11, 7 - Gear^.Frame, Gear^.Tag, Gear^.Angle);
vgtFire: if (Gear^.State and gstTmpFlag) = 0 then
DrawSprite(sprFlame, round(Gear^.X) + WorldDx - 8, round(Gear^.Y) + WorldDy, (RealTicks shr 6 + Gear^.Frame) mod 8)
else
@@ -956,10 +959,10 @@
exit;
if hasBorder or ((Theme <> 'Snow') and (Theme <> 'Christmas')) then
- for i:= 0 to Pred(vobCount * cScreenSpace div LAND_WIDTH) do
+ for i:= 0 to Pred(vobCount * cScreenSpace div 4096) do
AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake)
else
- for i:= 0 to Pred((vobCount * cScreenSpace div LAND_WIDTH) div 3) do
+ for i:= 0 to Pred((vobCount * cScreenSpace div 4096) div 3) do
AddVisualGear(cLeftScreenBorder + random(cScreenSpace), random(1024+200) - 100 + LAND_HEIGHT, vgtFlake);
end;
--- a/hedgewars/uWorld.pas Thu Aug 30 12:47:41 2012 -0400
+++ b/hedgewars/uWorld.pas Thu Aug 30 13:02:19 2012 -0400
@@ -60,7 +60,8 @@
uCaptions,
uCursor,
uCommands,
- uMobile
+ uMobile,
+ uVideoRec
;
var cWaveWidth, cWaveHeight: LongInt;
@@ -80,6 +81,7 @@
stereoDepth: GLfloat;
isFirstFrame: boolean;
AMAnimType: LongInt;
+ recTexture: PTexture;
const cStereo_Sky = 0.0500;
cStereo_Horizon = 0.0250;
@@ -377,6 +379,8 @@
timeTexture:= nil;
FreeTexture(missionTex);
missionTex:= nil;
+ FreeTexture(recTexture);
+ recTexture:= nil;
end;
function GetAmmoMenuTexture(Ammo: PHHAmmo): PTexture;
@@ -954,7 +958,7 @@
//glPushMatrix;
//glScalef(1.0, 1.0, 1.0);
- if not isPaused then
+ if (not isPaused) and (GameType <> gmtRecord) then
MoveCamera;
if cStereoMode = smNone then
@@ -985,7 +989,7 @@
DrawWorldStereo(0, rmRightEye);
// detatch drawing from fbs
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
SetScale(cDefaultZoomLevel);
@@ -1329,7 +1333,7 @@
r.w:= 3;
DrawTextureFromRect(TeamHealthBarWidth + 16, cScreenHeight + DrawHealthY + smallScreenOffset, @r, HealthTex);
- if not highlight and not hasGone and (TeamHealth > 1) then
+ if not highlight and (not hasGone) and (TeamHealth > 1) then
for i:= 0 to cMaxHHIndex do
if Hedgehogs[i].Gear <> nil then
begin
@@ -1581,6 +1585,33 @@
end
end;
+{$IFDEF USE_VIDEO_RECORDING}
+// during video prerecording draw red blinking circle and text 'rec'
+if flagPrerecording then
+ begin
+ if recTexture = nil then
+ begin
+ s:= 'rec';
+ tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fntBig].Handle, Str2PChar(s), cWhiteColorChannels);
+ tmpSurface:= doSurfaceConversion(tmpSurface);
+ FreeTexture(recTexture);
+ recTexture:= Surface2Tex(tmpSurface, false);
+ SDL_FreeSurface(tmpSurface)
+ end;
+ DrawTexture( -(cScreenWidth shr 1) + 50, 20, recTexture);
+
+ // draw red circle
+ glDisable(GL_TEXTURE_2D);
+ Tint($FF, $00, $00, Byte(Round(127*(1 + sin(SDL_GetTicks()*0.007)))));
+ glBegin(GL_POLYGON);
+ for i:= 0 to 20 do
+ glVertex2f(-(cScreenWidth shr 1) + 30 + sin(i*2*Pi/20)*10, 35 + cos(i*2*Pi/20)*10);
+ glEnd();
+ Tint($FF, $FF, $FF, $FF);
+ glEnable(GL_TEXTURE_2D);
+ end;
+{$ENDIF}
+
SetScale(zoom);
// Cursor
@@ -1614,10 +1645,7 @@
uCursor.updatePosition();
{$ENDIF}
z:= round(200/zoom);
-if not PlacingHogs and (FollowGear <> nil) and (not isCursorVisible) and (not bShowAmmoMenu) and (not fastUntilLag) then
- if (not autoCameraOn) then
- FollowGear:= nil
- else
+if not PlacingHogs and (FollowGear <> nil) and (not isCursorVisible) and (not bShowAmmoMenu) and (not fastUntilLag) and autoCameraOn then
if ((abs(CursorPoint.X - prevPoint.X) + abs(CursorPoint.Y - prevpoint.Y)) > 4) then
begin
FollowGear:= nil;
@@ -1764,8 +1792,14 @@
if (not cHasFocus) and (GameState <> gsConfirm) then
ParseCommand('quit', true);
-if not cHasFocus then DampenAudio()
-else UndampenAudio();
+{$IFDEF USE_VIDEO_RECORDING}
+// do not change volume during prerecording as it will affect sound in video file
+if not flagPrerecording then
+{$ENDIF}
+ begin
+ if not cHasFocus then DampenAudio()
+ else UndampenAudio();
+ end;
end;
procedure SetUtilityWidgetState(ammoType: TAmmoType);
@@ -1822,6 +1856,7 @@
procedure initModule;
begin
fpsTexture:= nil;
+ recTexture:= nil;
FollowGear:= nil;
WindBarWidth:= 0;
bShowAmmoMenu:= false;
@@ -1853,7 +1888,9 @@
FreeTexture(timeTexture);
timeTexture:= nil;
FreeTexture(missionTex);
- missionTex:= nil
+ missionTex:= nil;
+ FreeTexture(recTexture);
+ recTexture:= nil;
end;
end.
--- a/project_files/hedgewars.pro Thu Aug 30 12:47:41 2012 -0400
+++ b/project_files/hedgewars.pro Thu Aug 30 13:02:19 2012 -0400
@@ -104,7 +104,12 @@
../QTfrontend/ui/dialog/input_password.h \
../QTfrontend/ui/widget/colorwidget.h \
../QTfrontend/model/HatModel.h \
- ../QTfrontend/model/GameStyleModel.h
+ ../QTfrontend/model/GameStyleModel.h \
+ ../QTfrontend/util/libav_iteraction.h \
+ ../QTfrontend/ui/page/pagevideos.h \
+ ../QTfrontend/net/recorder.h \
+ ../QTfrontend/ui/dialog/ask_quit.h \
+ ../QTfrontend/ui/dialog/upload_video.h
SOURCES += ../QTfrontend/model/ammoSchemeModel.cpp \
../QTfrontend/model/MapModel.cpp \
@@ -186,7 +191,12 @@
../QTfrontend/ui/dialog/input_password.cpp \
../QTfrontend/ui/widget/colorwidget.cpp \
../QTfrontend/model/HatModel.cpp \
- ../QTfrontend/model/GameStyleModel.cpp
+ ../QTfrontend/model/GameStyleModel.cpp \
+ ../QTfrontend/util/libav_iteraction.cpp \
+ ../QTfrontend/ui/page/pagevideos.cpp \
+ ../QTfrontend/net/recorder.cpp \
+ ../QTfrontend/ui/dialog/ask_quit.cpp \
+ ../QTfrontend/ui/dialog/upload_video.cpp
win32 {
SOURCES += ../QTfrontend/xfire.cpp
Binary file share/hedgewars/Data/Graphics/Flags/cpu_plain.png has changed
Binary file share/hedgewars/Data/Graphics/Graves/dragonball.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Graphics/Graves/dragonball.svg Thu Aug 30 13:02:19 2012 -0400
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.2 r9819"
+ width="32"
+ height="256"
+ sodipodi:docname="Dragonball.svg"
+ inkscape:export-filename="/home/alumno/Dragonball.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6">
+ <linearGradient
+ id="linearGradient4055-9"
+ inkscape:collect="always">
+ <stop
+ id="stop4057-4"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop4059-43"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ y2="44.056526"
+ x2="130.48477"
+ y1="44.056526"
+ x1="-9.8052101"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3900-8"
+ xlink:href="#linearGradient4055-9"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4055-3-0-4"
+ inkscape:collect="always">
+ <stop
+ id="stop4057-6-7-5"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop4059-2-5-2"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ y2="44.056526"
+ x2="130.48477"
+ y1="44.056526"
+ x1="-9.8052101"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3488-6"
+ xlink:href="#linearGradient4055-3-0-4"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4055-80"
+ inkscape:collect="always">
+ <stop
+ id="stop4057-7"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop4059-1"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ y2="44.056526"
+ x2="130.48477"
+ y1="44.056526"
+ x1="-9.8052101"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4091-2"
+ xlink:href="#linearGradient4055-80"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4055-8-2-8"
+ inkscape:collect="always">
+ <stop
+ id="stop4057-8-7-0"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop4059-4-7-8"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4055-8-2-8"
+ id="linearGradient4113-9-1"
+ gradientUnits="userSpaceOnUse"
+ x1="-9.8052101"
+ y1="44.056526"
+ x2="130.48477"
+ y2="44.056526" />
+ <linearGradient
+ id="linearGradient4055-3"
+ inkscape:collect="always">
+ <stop
+ id="stop4057-6"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop4059-2"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ y2="44.056526"
+ x2="130.48477"
+ y1="44.056526"
+ x1="-9.8052101"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4091-0"
+ xlink:href="#linearGradient4055-3"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4055-8"
+ inkscape:collect="always">
+ <stop
+ id="stop4057-8"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop4059-4"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ y2="44.056526"
+ x2="130.48477"
+ y1="44.056526"
+ x1="-9.8052101"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4091-1"
+ xlink:href="#linearGradient4055-8"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1024"
+ inkscape:window-height="576"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="0.92187498"
+ inkscape:cx="9.5337118"
+ inkscape:cy="163.22073"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <g
+ id="g3100">
+ <path
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+ d="m 130.48477,44.056526 a 70.144989,72.407738 0 1 1 -140.2899801,0 70.144989,72.407738 0 1 1 140.2899801,0 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+ d="m 130.48477,44.056526 a 70.144989,72.407738 0 1 1 -140.2899801,0 70.144989,72.407738 0 1 1 140.2899801,0 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-7"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cssscsscc"
+ inkscape:connector-curvature="0"
+ id="path4077"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+ inkscape:transform-center-y="-0.35951858"
+ inkscape:transform-center-x="0.015096207"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.5590267"
+ sodipodi:arg1="0.93070817"
+ sodipodi:r2="1.9533451"
+ sodipodi:r1="3.9066894"
+ sodipodi:cy="12.666666"
+ sodipodi:cx="13.466667"
+ sodipodi:sides="5"
+ id="path4071"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <g
+ transform="translate(-1.5272255e-8,-32.508047)"
+ id="g3100-0">
+ <path
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-1"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-7-52"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cssscsscc"
+ inkscape:connector-curvature="0"
+ id="path4077-67"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+ inkscape:transform-center-y="-0.35951858"
+ inkscape:transform-center-x="0.015096207"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.5590267"
+ sodipodi:arg1="0.93070817"
+ sodipodi:r2="1.9533451"
+ sodipodi:r1="3.9066894"
+ sodipodi:cy="12.666666"
+ sodipodi:cx="13.466667"
+ sodipodi:sides="5"
+ id="path4071-7"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <g
+ transform="translate(-1.5272256e-8,31.848953)"
+ id="g3100-1">
+ <path
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-11"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-7-9"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cssscsscc"
+ inkscape:connector-curvature="0"
+ id="path4077-9"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+ inkscape:transform-center-y="-0.35951858"
+ inkscape:transform-center-x="0.015096207"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.5590267"
+ sodipodi:arg1="0.93070817"
+ sodipodi:r2="1.9533451"
+ sodipodi:r1="3.9066894"
+ sodipodi:cy="12.666666"
+ sodipodi:cx="13.466667"
+ sodipodi:sides="5"
+ id="path4071-51"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <g
+ id="g3100-5"
+ transform="translate(0,96.26138)">
+ <path
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-8"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-7-86"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cssscsscc"
+ inkscape:connector-curvature="0"
+ id="path4077-4"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+ inkscape:transform-center-y="-0.35951858"
+ inkscape:transform-center-x="0.015096207"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.5590267"
+ sodipodi:arg1="0.93070817"
+ sodipodi:r2="1.9533451"
+ sodipodi:r1="3.9066894"
+ sodipodi:cy="12.666666"
+ sodipodi:cx="13.466667"
+ sodipodi:sides="5"
+ id="path4071-5"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <g
+ transform="translate(-1.5272255e-8,63.753333)"
+ id="g3100-0-0">
+ <path
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-1-1"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-7-52-0"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cssscsscc"
+ inkscape:connector-curvature="0"
+ id="path4077-67-5"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+ inkscape:transform-center-y="-0.35951858"
+ inkscape:transform-center-x="0.015096207"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.5590267"
+ sodipodi:arg1="0.93070817"
+ sodipodi:r2="1.9533451"
+ sodipodi:r1="3.9066894"
+ sodipodi:cy="12.666666"
+ sodipodi:cx="13.466667"
+ sodipodi:sides="5"
+ id="path4071-7-0"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <g
+ transform="translate(-1.5272256e-8,128.11033)"
+ id="g3100-1-8">
+ <path
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-11-9"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-7-9-4"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cssscsscc"
+ inkscape:connector-curvature="0"
+ id="path4077-9-1"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+ inkscape:transform-center-y="-0.35951858"
+ inkscape:transform-center-x="0.015096207"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.5590267"
+ sodipodi:arg1="0.93070817"
+ sodipodi:r2="1.9533451"
+ sodipodi:r1="3.9066894"
+ sodipodi:cy="12.666666"
+ sodipodi:cx="13.466667"
+ sodipodi:sides="5"
+ id="path4071-51-2"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <g
+ id="g3100-5-3"
+ transform="translate(-1.5272253e-8,160.30523)">
+ <path
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-8-8"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ sodipodi:ry="72.407738"
+ sodipodi:rx="70.144989"
+ sodipodi:cy="44.056526"
+ sodipodi:cx="60.339779"
+ id="path3060-7-86-6"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="cssscsscc"
+ inkscape:connector-curvature="0"
+ id="path4077-4-0"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)"
+ inkscape:transform-center-y="-0.35951858"
+ inkscape:transform-center-x="0.015096207"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.5590267"
+ sodipodi:arg1="0.93070817"
+ sodipodi:r2="1.9533451"
+ sodipodi:r1="3.9066894"
+ sodipodi:cy="12.666666"
+ sodipodi:cx="13.466667"
+ sodipodi:sides="5"
+ id="path4071-5-7"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="star" />
+ </g>
+ <g
+ transform="translate(8.8208575,190.52706)"
+ id="g3100-1-8-8">
+ <g
+ id="g3339"
+ transform="translate(-8.8208575,1.5340622)">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffac24;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3060-11-9-8"
+ sodipodi:cx="60.339779"
+ sodipodi:cy="44.056526"
+ sodipodi:rx="70.144989"
+ sodipodi:ry="72.407738"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ transform="matrix(0.17677671,0,0,0.17125242,5.7333328,42.12326)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ed9206;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3060-7-9-4-8"
+ sodipodi:cx="60.339779"
+ sodipodi:cy="44.056526"
+ sodipodi:rx="70.144989"
+ sodipodi:ry="72.407738"
+ d="m 130.48477,44.056526 c 0,39.98969 -31.404983,72.407734 -70.144991,72.407734 -38.740008,0 -70.1449891,-32.418044 -70.1449891,-72.407734 0,-39.9896891 31.4049811,-72.407738 70.1449891,-72.407738 38.740008,0 70.144991,32.4180489 70.144991,72.407738 z"
+ transform="matrix(0.15467962,0,0,0.14984587,6.4957768,42.439091)" />
+ <path
+ style="fill:#ed8306;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 19.085438,59.142083 c -0.36023,-0.1397 -0.589073,-0.503672 -0.658968,-1.048089 -0.15772,-1.228515 2.606903,-2.373056 3.740935,-4.08299 1.116204,-1.683048 0.58952,-3.935901 1.989235,-4.615917 0.701981,-0.341041 1.092365,-0.417866 1.519378,-0.299001 0.539091,0.150054 0.761059,0.492497 0.807314,1.24549 0.14374,2.339891 -2.644356,6.671744 -5.339,8.295178 -0.719612,0.433543 -1.653173,0.662676 -2.058892,0.505335 z"
+ id="path4077-9-1-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssscsscc" />
+ <path
+ sodipodi:type="star"
+ style="opacity:0.55;fill:#d40000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path4071-51-2-5"
+ sodipodi:sides="5"
+ sodipodi:cx="13.466667"
+ sodipodi:cy="12.666666"
+ sodipodi:r1="3.9066894"
+ sodipodi:r2="1.9533451"
+ sodipodi:arg1="0.93070817"
+ sodipodi:arg2="1.5590267"
+ inkscape:flatsided="false"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="M 15.800001,15.799999 13.489657,14.619876 11.20773,15.854051 11.616158,13.292105 9.7372337,11.503245 12.3,11.099999 l 1.120688,-2.3397518 1.175448,2.3127258 2.571548,0.342814 -1.8363,1.83259 z"
+ inkscape:transform-center-x="0.015096207"
+ inkscape:transform-center-y="-0.35951858"
+ transform="matrix(1.3174211,0,0,1.2399999,-1.1801897,33.032817)" />
+ </g>
+ </g>
+ <path
+ style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 5.6818853,52.944892 c -1.4249208,-4.670814 -0.1790584,-8.097449 1.725719,-10.495306 2.0559768,-2.588197 5.0634737,-4.368771 8.4063917,-4.281193 2.450497,-0.111055 5.480318,1.374625 5.480318,1.374625 -0.07868,0.78593 -6.13236,2.602482 -8.919506,5.655618 -2.283219,2.501117 -5.0641461,7.081358 -4.7383419,11.096412 -0.6809241,-0.87477 -1.4647048,-2.11081 -1.9545808,-3.350156 z"
+ id="path4121-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscsscc" />
+ <path
+ style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="M 5.9583102,85.256922 C 5.4238859,83.650628 4.4751276,79.169801 5.5109031,77.40561 6.5395157,75.653619 7.397708,73.991223 8.317305,73.071625 c 1.079063,-1.079062 3.838619,-3.17952 9.462215,-2.843943 1.806813,0.204535 4.457553,1.7441 4.457553,1.7441 -0.07868,0.78593 -5.183207,3.531776 -7.970353,6.584911 -2.14499,2.349697 -4.7242734,7.983314 -6.5030137,9.559525 -0.3693871,-0.482911 -1.133301,-1.17148 -1.8053961,-2.859296 z"
+ id="path4121-4-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csscsscc" />
+ <path
+ style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 6.0993866,117.32562 c 3.5759918,-9.01347 6.7157204,-11.897 11.9170884,-14.69367 1.806813,-0.27485 4.361674,1.40853 4.361674,1.40853 -0.07868,0.78593 -4.173901,3.91587 -6.961045,6.96901 -2.708171,2.96662 -6.4118227,14.10633 -9.3177174,6.31613 z"
+ id="path4121-4-6-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccssc" />
+ <path
+ style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 8.0353418,152.69557 c 8.9452112,-4.69892 12.8040302,-14.72543 12.7320592,-16.85095 1.758874,0.25249 4.335587,3.83076 4.335587,3.83076 -0.07868,0.78593 -1.835084,6.44844 -4.622233,9.50157 -1.983167,2.17243 -5.268932,6.41929 -8.885039,5.90193 -1.32197,-0.18914 -2.1543301,-0.80776 -3.5603742,-2.38331 z"
+ id="path4121-4-6-5-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsscc" />
+ <path
+ style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 10.441003,186.42942 c 3.222707,-1.69289 5.890978,-4.0906 7.895906,-6.56495 3.560116,-4.39366 5.17465,-9.02893 5.128608,-10.38869 1.423297,1.21127 2.90704,4.80317 2.90704,4.80317 0.395896,2.95542 0.537798,6.99081 -2.249351,10.04395 -2.060273,2.25689 -5.592916,3.90658 -9.359327,3.58681 -1.248968,-0.10604 -4.171671,-1.08589 -4.322876,-1.48029 z"
+ id="path4121-4-6-5-9-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscsssc" />
+ <path
+ style="opacity:0.41999996;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ d="m 13.077671,219.87312 c 8.945211,-4.69892 10.987149,-15.6465 10.915178,-17.77202 1.423298,1.01952 2.379707,4.51894 2.379707,4.51894 -0.07868,0.78593 0.864467,4.39019 -1.055682,8.05118 -0.783125,1.49312 -3.834308,4.50955 -7.291243,5.31355 -1.302673,0.30297 -3.01998,0.17502 -4.94796,-0.11165 z"
+ id="path4121-4-6-5-9-2-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssc" />
+</svg>
Binary file share/hedgewars/Data/Graphics/botlevels.png has changed
--- a/share/hedgewars/Data/Locale/en.txt Thu Aug 30 12:47:41 2012 -0400
+++ b/share/hedgewars/Data/Locale/en.txt Thu Aug 30 13:02:19 2012 -0400
@@ -492,7 +492,7 @@
04:36=Well, sometimes you're just too bad in aiming. Get|some assistance using modern day technology.|Attack: Activate
04:37=Don't fear the daylight. It will just last one turn|but will enable you to absorb the damage you do to|other hogs.|Attack: Activate
04:38=The sniper rifle can be the most devastating weapon|in your whole arsenal, however it's very ineffective|at close quarters. The damage dealt increases with|the distance to its target.|Attack: Shoot (twice)
-04:39=Fly to other parts of the map using the flying|saucer. This hard to master utility is able to|take you to almost any position on the battlefield.|Attack: Activate|Up/Left/Right: Apply force in one direction|Long Jump: Drop grenades or similar weapons
+04:39=Fly to other parts of the map using the flying|saucer. This hard to master utility can|take you to almost any position on the battlefield.|Attack: Activate|Up/Left/Right: Apply force in one direction|Long Jump: Drop grenades or similar weapons
04:40=Set some ground on fire using this bottle filled|with (soon to be) burning liquid.|Attack: Hold to shoot with more power
04:41=The evidence nature might even top the flying|saucer. Birdy can carry your hog around and|drop eggs on your enemies!|Be quick, as using Birdy eats into your turn|time!|Attack: Activate and drop eggs|Up/Left/Right: Flap in one direction
04:42=This portable portal device is capable|of instantly transporting you, your enemies,|or your weaponry between two points on the|terrain.|Use it wisely and your campaign will be a...|HUGE SUCCESS!|Attack: Shoot a portal|Switch: Cycle portal colours
@@ -506,7 +506,7 @@
04:50=Is someone hiding underground?|Dig them out with a drill strike!|Timer controls how far it will dig.
04:51=Get in a free shot by hurling a ball of mud.|Stings a bit, and knocks hogs back.
04:52=UNUSED
-04:53=Go on an adventure through time and space,|while leaving your comrades to fight on alone.|Be prepared to return at any time,|or for Sudden Death or if they are all defeated.|Disclaimer. Does not function in Sudden Death,|if you are alone, or if you are a King.
+04:53=Take a trip through time and space,|while leaving your comrades to fight on alone.|Be prepared to return at any time,|or for Sudden Death or if they are all defeated.|Disclaimer. Does not function in Sudden Death,|if you are alone, or if you are a King.
04:54=INCOMPLETE
04:55=Spray a stream of sticky flakes.|Build bridges, bury enemies, seal off tunnels.|Be careful you don't get any on you!
--- a/share/hedgewars/Data/Locale/hedgewars_zh_CN.ts Thu Aug 30 12:47:41 2012 -0400
+++ b/share/hedgewars/Data/Locale/hedgewars_zh_CN.ts Thu Aug 30 13:02:19 2012 -0400
@@ -2,325 +2,240 @@
<!DOCTYPE TS>
<TS version="2.0" language="zh_CN">
<context>
- <name>AmmoSchemeModel</name>
- <message>
- <source>new</source>
- <translation>新建</translation>
- </message>
- <message>
- <source>copy of</source>
- <translation>副本</translation>
- </message>
-</context>
-<context>
- <name>DrawMapWidget</name>
- <message>
- <source>File error</source>
- <translation>文件错误</translation>
- </message>
- <message>
- <source>Cannot open file '%1' for writing</source>
- <translatorcomment>无法打开文件 '%1' 写入</translatorcomment>
- <translation>无法打开要写入的文件 '%1'</translation>
- </message>
- <message>
- <source>Cannot read file '%1'</source>
- <translation>无法读取文件 '%1'</translation>
- </message>
-</context>
-<context>
<name>FreqSpinBox</name>
<message>
+ <location filename="../../../../QTfrontend/misc.h" line="38"/>
<source>Never</source>
<translation>从不</translation>
</message>
<message numerus="yes">
+ <location filename="../../../../QTfrontend/misc.h" line="40"/>
<source>Every %1 turn</source>
<translation>
- <numerusform>每 %1 个回合</numerusform>
+ <numerusform>每隔 %1 回合</numerusform>
</translation>
</message>
</context>
<context>
<name>GameCFGWidget</name>
<message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="129"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="129"/>
<source>Illegal ammo scheme</source>
<translation>无法使用此弹药设置</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="57"/>
<source>Edit schemes</source>
<translation>修改游戏设置</translation>
</message>
- <message>
- <source>Edit weapons</source>
- <translation>修改武器</translation>
- </message>
- <message>
- <source>When this option is enabled selecting a game scheme will auto-select a weapon</source>
- <translation>使用此项则游戏框架自动选择武器配备</translation>
- </message>
- <message>
- <source>Game Options</source>
- <translation>游戏选项</translation>
- </message>
</context>
<context>
- <name>HWChatWidget</name>
- <message>
- <source>%1 *** %2 has been removed from your ignore list</source>
- <translation type="obsolete">%1 *** %2 已经从您的忽略列表中移除</translation>
- </message>
- <message>
- <source>%1 *** %2 has been added to your ignore list</source>
- <translation type="obsolete">%1 *** %2 已经添加到您的忽略列表中</translation>
- </message>
- <message>
- <source>%1 *** %2 has been removed from your friends list</source>
- <translation type="obsolete">%1 *** %2 已经从您的朋友列表中移除</translation>
- </message>
+ <name>GameUIConfig</name>
<message>
- <source>%1 *** %2 has been added to your friends list</source>
- <translation type="obsolete">%1 *** %2 已经添加到您的朋友列表中</translation>
- </message>
- <message>
- <source>%1 has been removed from your ignore list</source>
- <translation>%1 已从您的忽略列表中移除</translation>
- </message>
- <message>
- <source>%1 has been added to your ignore list</source>
- <translation>%1 已被添加到您的忽略列表中</translation>
- </message>
- <message>
- <source>%1 has been removed from your friends list</source>
- <translation>%1 已从您的好友列表中移除</translation>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+ <source>Error</source>
+ <translation type="obsolete">错误</translation>
</message>
<message>
- <source>%1 has been added to your friends list</source>
- <translation>%1 已加入您的好友列表</translation>
- </message>
- <message>
- <source>Stylesheet imported from %1</source>
- <translation>已从 %1 导入样式表</translation>
- </message>
- <message>
- <source>Enter %1 if you want to use the current StyleSheet in future, enter %2 to reset!</source>
- <translation>键入 %1 以使用当前的样式表,键入 %2 以重置!</translation>
- </message>
- <message>
- <source>Couldn't read %1</source>
- <translation>无法读取 %1</translation>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+ <source>Cannot create directory %1</source>
+ <translation type="obsolete">不能创建路径</translation>
</message>
<message>
- <source>StyleSheet discarded</source>
- <translation>已丢弃样式表</translation>
- </message>
- <message>
- <source>StyleSheet saved to %1</source>
- <translation>样式表已保存到 %1</translation>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+ <source>Quit</source>
+ <translation type="obsolete">退出</translation>
</message>
<message>
- <source>Failed to save StyleSheet to %1</source>
- <translation>保存样式表到 %1 失败</translation>
- </message>
- <message>
- <source>%1 is not a valid command!</source>
- <translation>%1 不是一个有效的命令!</translation>
- </message>
- <message>
- <source>Kicking %1 ...</source>
- <translation>正在踢出 %1 ...</translation>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+ <source>Cannot save options to file %1</source>
+ <translation type="obsolete">不能把选项保存到 %1</translation>
</message>
</context>
<context>
<name>HWForm</name>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="639"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+ <source>Please, select demo from the list above</source>
+ <translation type="obsolete">请选择一个DEMO</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="484"/>
<source>OK</source>
<translation>确认</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+ <source>Please, select server from the list above</source>
+ <translation type="obsolete">请选择一个服务器</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="483"/>
+ <source>Please, select record from the list above</source>
+ <translation>请选择一个记录</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="794"/>
<source>Cannot save record to file %1</source>
<translation>无法录入文件 %1</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="640"/>
<source>Unable to start the server</source>
<translation>开启服务端出现错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="256"/>
<source>new</source>
- <translation type="obsolete">新</translation>
- </message>
- <message>
- <source>Please select record from the list above</source>
- <translation>请选择一个记录</translation>
- </message>
- <message>
- <source>DefaultTeam</source>
- <translation>默认队伍</translation>
- </message>
- <message>
- <source>Hedgewars Demo File</source>
- <comment>File Types</comment>
- <translation>刺猬大作战回放文件</translation>
- </message>
- <message>
- <source>Hedgewars Save File</source>
- <comment>File Types</comment>
- <translation>刺猬大作战存档文件</translation>
- </message>
- <message>
- <source>Demo name</source>
- <translation>回放录像名称</translation>
- </message>
- <message>
- <source>Demo name:</source>
- <translation>回放录像名称:</translation>
- </message>
- <message>
- <source>Game aborted</source>
- <translation>游戏被中断</translation>
- </message>
- <message>
- <source>Password</source>
- <translation>密码</translation>
- </message>
- <message>
- <source>Your nickname %1 is
-registered on Hedgewars.org
-Please provide your password below
-or pick another nickname in game config:</source>
- <translation>您的昵称%1
-在Hedgewars.org已注册
-请输入您的密码
-或从游戏配置中选择另一个昵称:</translation>
- </message>
- <message>
- <source>No password supplied.</source>
- <translation>没有填写密码.</translation>
- </message>
- <message>
- <source>Nickname</source>
- <translation>昵称</translation>
- </message>
- <message>
- <source>Some one already uses
- your nickname %1
-on the server.
-Please pick another nickname:</source>
- <translation>您的昵称 %1
-以被其他人
-在服务器上使用
-请选择另一个昵称:</translation>
- </message>
- <message>
- <source>No nickname supplied.</source>
- <translation>没有填写昵称。</translation>
+ <translation>新</translation>
</message>
</context>
<context>
<name>HWGame</name>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+ <source>Error</source>
+ <translation type="obsolete">错误</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+ <source>Unable to start the server: %1.</source>
+ <translation type="obsolete">开启服务端出现错误: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/game.cpp" line="280"/>
<source>en.txt</source>
<translation>zh_CN.txt</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/game.cpp" line="0"/>
+ <source>Cannot save demo to file %1</source>
+ <translation type="obsolete">不能把demo保存为 %1</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/game.cpp" line="0"/>
+ <source>Quit</source>
+ <translation type="obsolete">退出</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/game.cpp" line="305"/>
<source>Cannot open demofile %1</source>
<translation>DEMO %1 打不开</translation>
</message>
+ <message>
+ <location filename="../../../../QTfrontend/game.cpp" line="0"/>
+ <source>Unable to run engine: %1 (</source>
+ <translation type="obsolete">引擎无法启动: %1 (</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/game.cpp" line="147"/>
+ <source>Error reading training config file</source>
+ <translation>训练设置文件无法读取</translation>
+ </message>
</context>
<context>
<name>HWMapContainer</name>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="90"/>
<source>Map</source>
<translation>地图</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="110"/>
<source>Themes</source>
<translation>主题</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="93"/>
<source>Filter</source>
<translation>过滤</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="97"/>
<source>All</source>
<translation>全部</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="98"/>
<source>Small</source>
<translation>小型</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="99"/>
<source>Medium</source>
<translation>中型</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="100"/>
<source>Large</source>
<translation>大型</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="101"/>
<source>Cavern</source>
<translation>洞穴</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="102"/>
<source>Wacky</source>
- <translation>险峻</translation>
- </message>
- <message>
- <source>Type</source>
- <translation>类型</translation>
+ <translation>曲折</translation>
</message>
+</context>
+<context>
+ <name>HWNet</name>
<message>
- <source>Small tunnels</source>
- <translation>小型隧道</translation>
- </message>
- <message>
- <source>Medium tunnels</source>
- <translation>中型隧道</translation>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+ <source>Error</source>
+ <translation type="obsolete">错误</translation>
</message>
<message>
- <source>Large tunnels</source>
- <translation>大型隧道</translation>
- </message>
- <message>
- <source>Small floating islands</source>
- <translation>小型漂浮岛屿</translation>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+ <source>The host was not found. Please check the host name and port settings.</source>
+ <translation type="obsolete">未发现主机。请检查主机名和端口设置。</translation>
</message>
<message>
- <source>Medium floating islands</source>
- <translation>中型漂浮岛屿</translation>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+ <source>Connection refused</source>
+ <translation type="obsolete">连接被拒绝</translation>
+ </message>
+</context>
+<context>
+ <name>HWNetServer</name>
+ <message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+ <source>Error</source>
+ <translation type="obsolete">错误</translation>
</message>
<message>
- <source>Large floating islands</source>
- <translation>大型漂浮岛屿</translation>
- </message>
- <message>
- <source>Seed</source>
- <translation>作种</translation>
- </message>
- <message>
- <source>Set</source>
- <translation>设定</translation>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="0"/>
+ <source>Unable to start the server: %1.</source>
+ <translation type="obsolete">无法启动服务端: %1.</translation>
</message>
</context>
<context>
<name>HWNetServersModel</name>
<message>
+ <location filename="../../../../QTfrontend/netserverslist.cpp" line="45"/>
<source>Title</source>
- <translation>名称</translation>
+ <translation>标题</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/netserverslist.cpp" line="46"/>
<source>IP</source>
<translation>IP</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/netserverslist.cpp" line="47"/>
<source>Port</source>
<translation>端口</translation>
</message>
@@ -328,177 +243,109 @@
<context>
<name>HWNewNet</name>
<message>
- <source>The host was not found. Please check the host name and port settings.</source>
- <translation>没找到主机。请检查主机名和端口设置。</translation>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="187"/>
+ <source>Error</source>
+ <translation type="obsolete">错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="192"/>
+ <source>The host was not found. Please check the host name and port settings.</source>
+ <translation>错误没找到这个主机。请检查主机名和端口设置。</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="195"/>
<source>Connection refused</source>
<translation>连接被拒绝</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="402"/>
+ <source>*** %1 joined</source>
+ <translation>*** %1 加入</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="439"/>
+ <source>*** %1 left</source>
+ <translation>*** %1 离开</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="441"/>
+ <source>*** %1 left (%2)</source>
+ <translation>*** %1 离开 (%2)</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="531"/>
<source>Quit reason: </source>
<translation>退出原因:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="350"/>
<source>Room destroyed</source>
<translation>房间损坏</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="357"/>
<source>You got kicked</source>
<translation>被踢出</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="452"/>
<source>Password</source>
- <translation type="obsolete">密码</translation>
- </message>
- <message>
- <source>Your nickname %1 is
-registered on Hedgewars.org
-Please provide your password
-or pick another nickname:</source>
- <translation type="obsolete">您的昵称%1
-在Hedgewars.org已注册
-请输入您的密码
-或选择另一个昵称:</translation>
- </message>
- <message>
- <source>%1 *** %2 has joined the room</source>
- <translation>%1 *** %2 进入这个房间了</translation>
- </message>
- <message>
- <source>%1 *** %2 has joined</source>
- <translation>%1 *** %2 加入了</translation>
+ <translation>密码</translation>
</message>
<message>
- <source>%1 *** %2 has left (%3)</source>
- <translation>%1 *** %2 离开了(%3)</translation>
- </message>
- <message>
- <source>%1 *** %2 has left</source>
- <translation>%1 *** %2 离开了</translation>
- </message>
- <message>
- <source>Your nickname %1 is
-registered on Hedgewars.org
-Please provide your password below
-or pick another nickname in game config:</source>
- <translation type="obsolete">您的昵称%1
-在Hedgewars.org已注册
-请输入您的密码
-或从游戏配置中选择另一个昵称:</translation>
- </message>
- <message>
- <source>Nickname</source>
- <translation type="obsolete">昵称</translation>
- </message>
- <message>
- <source>User quit</source>
- <translation>用户退出</translation>
+ <location filename="../../../../QTfrontend/newnetclient.cpp" line="452"/>
+ <source>Enter your password:</source>
+ <translation>输入你的密码:</translation>
</message>
</context>
<context>
<name>KB</name>
<message>
+ <location filename="../../../../QTfrontend/KB.h" line="32"/>
<source>SDL_ttf returned error while rendering text, most propably it is related to the bug in freetype2. It's recommended to update your freetype lib.</source>
- <translation>渲染文字时SDL_ttf 返回错误,可能有关freetype2的bug。建议升级 freetype。</translation>
+ <translation>SDL_ttf 返回错误-渲染文字失败,可能有关freetype2的bug。建议升级 freetype。</translation>
</message>
</context>
<context>
<name>PageAdmin</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="1063"/>
<source>Server message:</source>
- <translation type="obsolete">服务器信息:</translation>
- </message>
- <message>
- <source>Set message</source>
- <translation type="obsolete">设定信息</translation>
- </message>
- <message>
- <source>Clear Accounts Cache</source>
- <translation>清空账户缓存</translation>
- </message>
- <message>
- <source>Fetch data</source>
- <translation>获取数据</translation>
+ <translation>服务器信息:</translation>
</message>
<message>
- <source>Server message for latest version:</source>
- <translation>最新版本的服务器信息:</translation>
- </message>
- <message>
- <source>Server message for previous versions:</source>
- <translation>之前版本的服务器信息:</translation>
- </message>
- <message>
- <source>Latest version protocol number:</source>
- <translation>最新版本的通讯协议号码:</translation>
- </message>
- <message>
- <source>MOTD preview:</source>
- <translation>MOTD预览:</translation>
- </message>
- <message>
- <source>Set data</source>
- <translation>设定数据</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="1069"/>
+ <source>Set message</source>
+ <translation>设定信息</translation>
</message>
</context>
<context>
<name>PageConnecting</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="848"/>
<source>Connecting...</source>
<translation>连接中...</translation>
</message>
- <message>
- <source>Cancel</source>
- <translation type="obsolete">取消</translation>
- </message>
-</context>
-<context>
- <name>PageDrawMap</name>
- <message>
- <source>Undo</source>
- <translation>取消</translation>
- </message>
- <message>
- <source>Clear</source>
- <translation>清除</translation>
- </message>
- <message>
- <source>Load</source>
- <translation>读取</translation>
- </message>
- <message>
- <source>Save</source>
- <translation>保存</translation>
- </message>
- <message>
- <source>Load drawn map</source>
- <translation>读取已经绘制的地图</translation>
- </message>
- <message>
- <source>Drawn Maps (*.hwmap);;All files (*.*)</source>
- <translation type="obsolete">绘制的地图 (*.hwmap);;全部文件 (*.*)</translation>
- </message>
- <message>
- <source>Save drawn map</source>
- <translation>保存绘制的地图</translation>
- </message>
- <message>
- <source>Drawn Maps</source>
- <translation>绘制的地图</translation>
- </message>
- <message>
- <source>All files</source>
- <translation>全部文件</translation>
- </message>
</context>
<context>
<name>PageEditTeam</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Discard</source>
+ <translation type="obsolete">中止</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Save</source>
+ <translation type="obsolete">保存</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="106"/>
<source>General</source>
<translation>常规</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="107"/>
<source>Advanced</source>
<translation>进阶</translation>
</message>
@@ -506,393 +353,87 @@
<context>
<name>PageGameStats</name>
<message>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="101"/>
<source><p>The best shot award was won by <b>%1</b> with <b>%2</b> pts.</p></source>
- <translation type="obsolete"><p>最佳射手奖给与 <b>%1</b>:伤害 <b>%2</b>点。</p></translation>
- </message>
- <message numerus="yes">
- <source><p>The best killer is <b>%1</b> with <b>%2</b> kills in a turn.</p></source>
- <translation type="obsolete">
- <numerusform><p>最佳杀手是 <b>%1</b>单回合击杀刺猬数:<b>%2</b></p></numerusform>
- </translation>
+ <translation><p>最佳射手是<b>%1</b>。伤害 <b>%2</b>点。</p></translation>
</message>
<message numerus="yes">
- <source><p>A total of <b>%1</b> hedgehog(s) were killed during this round.</p></source>
- <translation type="obsolete">
- <numerusform><p>本轮总共有<b>%1</b>只刺猬被击杀</p></numerusform>
- </translation>
- </message>
- <message>
- <source>Details</source>
- <translation>细节</translation>
- </message>
- <message>
- <source>Health graph</source>
- <translation>健康值图形</translation>
- </message>
- <message>
- <source>Ranking</source>
- <translation>排名</translation>
- </message>
- <message>
- <source>The best shot award was won by <b>%1</b> with <b>%2</b> pts.</source>
- <translation>最佳射手<b>%1</b>制造了<b>%2</b>点伤害。</translation>
- </message>
- <message numerus="yes">
- <source>The best killer is <b>%1</b> with <b>%2</b> kills in a turn.</source>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="108"/>
+ <source><p>The best killer is <b>%1</b> with <b>%2</b> kills in a turn.</p></source>
<translation>
- <numerusform>最佳杀手 <b>%1</b> 完成了单回合<b>%2</b>次击杀。</numerusform>
+ <numerusform><p>最佳杀手<b>%1</b>:却敌<b>%2</b></p></numerusform>
</translation>
</message>
<message numerus="yes">
- <source>A total of <b>%1</b> hedgehog(s) were killed during this round.</source>
- <translation>
- <numerusform>总共 <b>%1</b> 只刺猬在本轮失去生命。</numerusform>
- </translation>
- </message>
- <message numerus="yes">
- <source>(%1 kill)</source>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="115"/>
+ <source><p>A total of <b>%1</b> hedgehog(s) were killed during this round.</p></source>
<translation>
- <numerusform>(%1 击杀)</numerusform>
- </translation>
- </message>
- <message>
- <source>(%1 kills)</source>
- <translation type="obsolete">(%1 击杀)</translation>
- </message>
- <message numerus="yes">
- <source><b>%1</b> thought it's good to shoot his own hedgehogs with <b>%2</b> pts.</source>
- <translation>
- <numerusform><b>%1</b>以为给自己的刺猬造成 <b>%2</b> 点创伤是小意思。</numerusform>
+ <numerusform><p>有<b>%1</b>个刺猬在此局失去生命。</p></numerusform>
</translation>
</message>
- <message numerus="yes">
- <source><b>%1</b> killed <b>%2</b> of his own hedgehogs.</source>
- <translation>
- <numerusform><b>%1</b> 整垮了 <b>%2</b> 只自己的刺猬。</numerusform>
- </translation>
- </message>
- <message numerus="yes">
- <source><b>%1</b> was scared and skipped turn <b>%2</b> times.</source>
- <translation>
- <numerusform><b>%1</b> 受惊了,共计 <b>%2</b> 次放弃。</numerusform>
- </translation>
- </message>
-</context>
-<context>
- <name>PageInGame</name>
- <message>
- <source>In game...</source>
- <translation type="unfinished"></translation>
- </message>
</context>
<context>
<name>PageMain</name>
<message>
- <source>Local Game (Play a game on a single computer)</source>
- <translation>单机游戏(在一台电脑上)</translation>
- </message>
- <message>
- <source>Network Game (Play a game across a network)</source>
- <translation>网络游戏(通过网络)</translation>
- </message>
- <message>
- <source>Simply pick the same color as a friend to play together as a team. Each of you will still control his or her own hedgehogs but they'll win or lose together.</source>
- <comment>Tips</comment>
- <translation>点击同色作为同一组的友军。控制权不分享,但是共同胜利/失败。</translation>
- </message>
- <message>
- <source>Some weapons might do only low damage but they can be a lot more devastating in the right situation. Try to use the Desert Eagle to knock multiple hedgehogs into the water.</source>
- <comment>Tips</comment>
- <translation>有些武器可能威力低下但是有毁灭性的效果。比如沙漠之鹰能把多个刺猬打入水中。</translation>
- </message>
- <message>
- <source>If you're unsure what to do and don't want to waste ammo, skip one round. But don't let too much time pass as there will be Sudden Death!</source>
- <comment>Tips</comment>
- <translation>加入不确定怎么做,不要浪费弹药,跳过此回合。但是注意突然时间!</translation>
- </message>
- <message>
- <source>Want to save ropse? Release the rope in mid air and then shoot again. As long as you don't touch the ground you'll reuse your rope without wasting ammo!</source>
- <comment>Tips</comment>
- <translation type="obsolete">保存绳子?在半空释放然后再次射出。只要不接触地面停止就可以继续使用同一根不会浪费!</translation>
- </message>
- <message>
- <source>If you'd like to keep others from using your preferred nickname on the official server, register an account at http://www.hedgewars.org/.</source>
- <comment>Tips</comment>
- <translation>如果您确定好了一个昵称不想让别人使用,那么在 http://www.hedgewars.org/. 注册一个帐号吧。</translation>
- </message>
- <message>
- <source>You're bored of default gameplay? Try one of the missions - they'll offer different gameplay depending on the one you picked.</source>
- <comment>Tips</comment>
- <translation>厌倦了默认的玩法?试试任务吧——每个任务都有不同的玩法。</translation>
- </message>
- <message>
- <source>By default the game will always record the last game played as a demo. Select 'Local Game' and pick the 'Demos' button on the lower right corner to play or manage them.</source>
- <comment>Tips</comment>
- <translation>默认情况下游戏记录最后的游戏作为Demo,选择单机游戏——然后Demo——然后点击右下角开始回放或者整理。</translation>
- </message>
- <message>
- <source>Hedgewars is Open Source and Freeware we create in our spare time. If you've got problems, ask on our forums but please don't expect 24/7 support!</source>
- <comment>Tips</comment>
- <translation>刺猬大作战是一个开放源代码的免费软件,它充分利用了我们的业余时间。如果您有问题,到论坛来吧,不过7×24小时支持不可能!</translation>
- </message>
- <message>
- <source>Hedgewars is Open Source and Freeware we create in our spare time. If you like it, help us with a small donation or contribute your own work!</source>
- <comment>Tips</comment>
- <translation>刺猬大作战是一个开放源代码的免费软件,它充分利用了我们的业余时间。如果您喜欢它,我们接受您的捐赠/感谢!</translation>
- </message>
- <message>
- <source>Hedgewars is Open Source and Freeware we create in our spare time. Share it with your family and friends as you like!</source>
- <comment>Tips</comment>
- <translation>刺猬大作战是一个开放源代码的免费软件,它充分利用了我们的业余时间。与他人分享它吧!</translation>
- </message>
- <message>
- <source>From time to time there will be official tournaments. Upcoming events will be announced at http://www.hedgewars.org/ some days in advance.</source>
- <comment>Tips</comment>
- <translation>官方的竞赛一直存在。临近时去 http://www.hedgewars.org/ 即可看到。</translation>
- </message>
- <message>
- <source>Hedgewars is available in many languages. If the translation in your language seems to be missing or outdated, feel free to contact us!</source>
- <comment>Tips</comment>
- <translation>Hedgewars 被翻译成多种语言,中文是刺猬大作战——同样为翻译名。如果您的语言翻译有什么缺失/过时/遗漏或任何问题,来联系我们吧!</translation>
- </message>
- <message>
- <source>Hedgewars can be run on lots of different operating systems including Microsoft Windows, Mac OS X and Linux.</source>
- <comment>Tips</comment>
- <translation>刺猬大作战可以运行的操作系统包括:GNU/Linux、Mac OS X、MicroSoft Windows。</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>Multiplayer</source>
+ <translation type="obsolete">多人游戏</translation>
</message>
<message>
- <source>Always remember you're able to set up your own games in local and network/online play. You're not restricted to the 'Simple Game' option.</source>
- <comment>Tips</comment>
- <translation>您可以建立自己的网络游戏/局域网游戏。不仅限于 "简单游戏" 选项。</translation>
- </message>
- <message>
- <source>Create an account on http://www.hedgewars.org/ to keep others from using your most favourite nickname while playing on the official server.</source>
- <comment>Tips</comment>
- <translation type="obsolete">在官方服务器 http://www.hedgewars.org/ 建立自己的帐号——就能一直使用最喜欢的昵称</translation>
- </message>
- <message>
- <source>While playing you should give yourself a short break at least once an hour.</source>
- <comment>Tips</comment>
- <translation>最好玩一个小时就休息一下,如果你要继续用电脑。</translation>
- </message>
- <message>
- <source>If your graphics card isn't able to provide hardware accelerated OpenGL, try to enable the low quality mode to improve performance.</source>
- <comment>Tips</comment>
- <translation>假如你的显卡不能提供OpenGL硬件加速,试着用降低效果的方式运行。</translation>
- </message>
- <message>
- <source>We're open to suggestions and constructive feedback. If you don't like something or got a great idea, let us know!</source>
- <comment>Tips</comment>
- <translation>我们接受意见和建设性反馈。假如您有好电子或者不喜欢的东西,告诉我们!</translation>
- </message>
- <message>
- <source>Especially while playing online be polite and always remember there might be some minors playing with or against you as well!</source>
- <comment>Tips</comment>
- <translation>特别是网络游戏,请有礼貌记住对方也和您一样是人!</translation>
- </message>
- <message>
- <source>Special game modes such as 'Vampirism' or 'Karma' allow you to develop completely new tactics. Try them in a custom game!</source>
- <comment>Tips</comment>
- <translation>特别游戏模式“吸血“、”因果报应“需要全新的战术。现在自定义游戏里试试!</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>Single Player</source>
+ <translation type="obsolete">单人游戏</translation>
</message>
<message>
- <source>The Windows version of Hedgewars supports Xfire. Make sure to add Hedgwars to its game list so your friends can see you playing.</source>
- <comment>Tips</comment>
- <translation type="obsolete">Windows版本的刺猬大作战支持Xfire。添加它到游戏列表里让您的朋友看到。</translation>
- </message>
- <message>
- <source>You should never install Hedgewars on computers you don't own (school, university, work, etc.). Please ask the responsible person instead!</source>
- <comment>Tips</comment>
- <translation>您不应该在不属于您的计算机上安装刺猬大作战——比如学校/工作场所等。您应当向计算机的负责人咨询!</translation>
- </message>
- <message>
- <source>Hedgewars can be perfect for short games during breaks. Just ensure you don't add too many hedgehogs or use an huge map. Reducing time and health might help as well.</source>
- <comment>Tips</comment>
- <translation>刺猬大作战适合短时间休息,不需要太多刺猬挤在大地图上。</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>Net game</source>
+ <translation type="obsolete">网络游戏</translation>
</message>
<message>
- <source>No hedgehogs were harmed in making this game.</source>
- <comment>Tips</comment>
- <translation>在制作这款游戏时没有任何刺猬受到伤害。</translation>
- </message>
- <message>
- <source>Connect one or more gamepads before launching the game to be able to assign their controls to your teams.</source>
- <comment>Tips</comment>
- <translation type="obsolete">在运行游戏前连接游戏板</translation>
- </message>
- <message>
- <source>Hedgewars is Open Source and Freeware we create in our spare time. If someone sold you the game, you should try get a refund!</source>
- <comment>Tips</comment>
- <translation>刺猬大作战是开放源代码的免费软件,用我们的闲暇时间创造。如果有人卖给你,那么应该把钱拿回来!</translation>
- </message>
- <message>
- <source>Create an account on %1 to keep others from using your most favourite nickname while playing on the official server.</source>
- <comment>Tips</comment>
- <translation>在 %1 建立一个帐号阻止其他人使用你喜欢的名称在官方服务器游戏。</translation>
- </message>
- <message>
- <source>There are three different jumps available. Tap [high jump] twice to do a very high/backwards jump.</source>
- <comment>Tips</comment>
- <translation>三种跳跃方式。点击[高跳]两次做出跳跃高度极限的后空翻。</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>Saved games</source>
+ <translation type="obsolete">存档</translation>
</message>
<message>
- <source>Afraid of falling off a cliff? Hold down [precise] to turn [left] or [right] without actually moving.</source>
- <comment>Tips</comment>
- <translation>害怕掉下悬崖?按住[精确]后再点击[左][右]就会只转身,不移动位置。</translation>
- </message>
- <message>
- <source>Some weapons require special strategies or just lots of training, so don't give up on a particular tool if you miss an enemy once.</source>
- <comment>Tips</comment>
- <translation>有些武器需要特殊策略或者仅仅是大量的练习,假如你一次失去准星,也不要放弃。</translation>
- </message>
- <message>
- <source>Most weapons won't work once they touch the water. The Homing Bee as well as the Cake are exceptions to this.</source>
- <comment>Tips</comment>
- <translation>多数武器不会在接触水之后生效。归巢的蜜蜂和蛋糕是例外。</translation>
- </message>
- <message>
- <source>The Old Limbuger only causes a small explosion. However the wind affected smelly cloud can poison lots of hogs at once.</source>
- <comment>Tips</comment>
- <translation>老干酪发射器造成小规模爆炸,然后产生随风移动的有毒云雾——能一次毒害多个刺猬。</translation>
- </message>
- <message>
- <source>The Piano Strike is the most damaging air strike. You'll lose the hedgehog performing it, so there's a huge downside as well.</source>
- <comment>Tips</comment>
- <translation>钢琴攻击是最大威力的空袭。弹奏钢琴的刺猬会跟着钢琴返回天堂。</translation>
- </message>
- <message>
- <source>The Homing Bee can be tricky to use. It's turn radius depends on it's velocity, so try to not use full power.</source>
- <comment>Tips</comment>
- <translation type="obsolete">归巢的蜜蜂有些技巧。它的回转半径和初速有关,最好不用全力发射。</translation>
- </message>
- <message>
- <source>Sticky Mines are a perfect tool to create small chain reactions knocking enemy hedgehogs into dire situations ... or water.</source>
- <comment>Tips</comment>
- <translation>黏着地雷是创造小范围连锁反应的绝佳武器。</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>Demos</source>
+ <translation type="obsolete">Demo</translation>
</message>
<message>
- <source>The Hammer is most effective when used on bridges or girders. Hit hogs will just break through the ground.</source>
- <comment>Tips</comment>
- <translation>锤是桥梁上/分界处最佳武器之一,只是刚刚好把刺猬打陷——如果没底就没办法了。</translation>
- </message>
- <message>
- <source>If you're stuck behind an enemy hedgehog, use the Hammer to free yourself without getting damaged by an explosion.</source>
- <comment>Tips</comment>
- <translation>假如对方刺猬把你堵住了,一锤打下去让自己轻松些。</translation>
- </message>
- <message>
- <source>The Cake's maximum walking distance depends on the ground it has to pass. Use [attack] to detonate it early.</source>
- <comment>Tips</comment>
- <translation>蛋糕的最大行走距离取决于地表。也可按下[攻击键]激活起爆。</translation>
- </message>
- <message>
- <source>The Flame Thrower is a weapon but it can be used for tunnel digging as well.</source>
- <comment>Tips</comment>
- <translation>火焰喷射器是一种武器,也是一种开路机器。</translation>
- </message>
- <message>
- <source>Use the Incinerating Grenade to temporary keep hedgehogs from passing terrain such as tunnels or platforms.</source>
- <comment>Tips</comment>
- <translation type="obsolete">燃烧瓶可以短时阻止刺猬通过特定区域(比如通道或平台)</translation>
- </message>
- <message>
- <source>Want to know who's behind the game? Click on the Hedgewars logo in the main menu to see the credits.</source>
- <comment>Tips</comment>
- <translation>想要知道谁是此游戏的幕后人员?点击主菜单的Hedgewars Logo就可以看到贡献者名单。</translation>
- </message>
- <message>
- <source>Like hedgewars? Become a fan on %1 or join our group at %2. You could follow us on %3 as well!</source>
- <comment>Tips</comment>
- <translation type="obsolete">喜欢刺猬大作战(hedgewars)?那么加入我们 %1 或者 %2.。你可以在 %3 跟随我们!</translation>
- </message>
- <message>
- <source>You can find your Hedgewars configuration files under "My Documents\Hedgewars". Create backups or take the files with you, but don't edit them by hand.</source>
- <comment>Tips</comment>
- <translation>你可以在( 我的文档\Hedgewars)里找到设置文件。可以创建备份,但不要手动修改。</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>Setup</source>
+ <translation type="obsolete">设置</translation>
</message>
<message>
- <source>You can find your Hedgewars configuration files under "Hedgewars" in your home directory. Create backups or take the files with you, but don't edit them by hand.</source>
- <comment>Tips</comment>
- <translation type="obsolete">你可以在家目录找到 .hedgewars。可以创建备份,但不要手动修改。</translation>
- </message>
- <message>
- <source>Connect one or more gamepads before starting the game to be able to assign their controls to your teams.</source>
- <comment>Tips</comment>
- <translation>在游戏开始前连接游戏手柄才能用它们操控你的队伍。</translation>
- </message>
- <message>
- <source>If your graphics card isn't able to provide hardware accelerated OpenGL, try to update the associated drivers.</source>
- <comment>Tips</comment>
- <translation>加入你的显卡不能使用OpenGL硬件加速,请升级相应驱动。</translation>
- </message>
- <message>
- <source>Like Hedgewars? Become a fan on %1 or join our group at %2. You could follow us on %3 as well!</source>
- <comment>Tips</comment>
- <translation type="obsolete">喜欢刺猬大作战(Hedgewars)吗?加入 %2 ,成为 %1 粉丝,也可以在 %3 跟随我们!</translation>
- </message>
- <message>
- <source>Feel free to draw your own graves, hats, flags or even maps and themes! But note that you'll have to share them somewhere to use them online.</source>
- <comment>Tips</comment>
- <translation>欢迎你自己绘制墓碑,帽子(头饰),旗帜或者地图,主题!但是记住,如果要在网上使用,你的必须把它们分享出来。</translation>
- </message>
- <message>
- <source>Really want to wear a specific hat? Donate to us and receive an exclusive hat of your choice!</source>
- <comment>Tips</comment>
- <translation>非常想要一个帽子?捐赠的话就给你!</translation>
- </message>
- <message>
- <source>Keep your video card drivers up to date to avoid issues playing the game.</source>
- <comment>Tips</comment>
- <translation>保持显卡驱动最新避免可能的麻烦。</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>About</source>
+ <translation type="obsolete">关于</translation>
</message>
<message>
- <source>You're able to associate Hedgewars related files (savegames and demo recordings) with the game to launch them right from your favorite file or internet browser.</source>
- <comment>Tips</comment>
- <translation>你可以关联刺猬大作战的相关文件(比如存档和回放)到本游戏以便在网络或文件浏览器中直接打开这些文件。</translation>
- </message>
- <message>
- <source>Want to save ropes? Release the rope in mid air and then shoot again. As long as you don't touch the ground you'll reuse your rope without wasting ammo!</source>
- <comment>Tips</comment>
- <translation>想要节省绳子?放开绳子之后再次发射,只要你不曾脱离绳子接触接触地面就可以继续使用同一根!</translation>
- </message>
- <message>
- <source>Like Hedgewars? Become a fan on %1 or follow us on %2!</source>
- <comment>Tips</comment>
- <translation>喜欢刺猬大作战Hedgewars吗?来 %1 或者 %2 追随我们吧!</translation>
- </message>
- <message>
- <source>You can find your Hedgewars configuration files under "Library/Application Support/Hedgewars" in your home directory. Create backups or take the files with you, but don't edit them by hand.</source>
- <comment>Tips</comment>
- <translation>在家目录的"Library/Application Support/Hedgewars"找到刺猬的配置文件。备份随你,但是不要手动编辑。</translation>
+ <location filename="../../../../QTfrontend/statsPage.cpp" line="0"/>
+ <source>Exit</source>
+ <translation type="obsolete">退出</translation>
</message>
<message>
- <source>You can find your Hedgewars configuration files under ".hedgewars" in your home directory. Create backups or take the files with you, but don't edit them by hand.</source>
- <comment>Tips</comment>
- <translation>在家目录的".hedgewars"找到刺猬的配置文件。备份随你,但是不要手动编辑。</translation>
- </message>
- <message>
- <source>The Windows version of Hedgewars supports Xfire. Make sure to add Hedgewars to its game list so your friends can see you playing.</source>
- <comment>Tips</comment>
- <translation>Windows版本的刺猬大作战支持Xfire。您可以添加刺猬大作战到它的游戏列表里让您的朋友看到。</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="81"/>
+ <source>Local Game (Play a game on a single computer)</source>
+ <translation>本地游戏(在一台电脑上)</translation>
</message>
<message>
- <source>Use the Molotov or Flame Thrower to temporary keep hedgehogs from passing terrain such as tunnels or platforms.</source>
- <comment>Tips</comment>
- <translation>使用燃烧瓶或火焰喷射器可以短时阻止刺猬通过特定区域(比如隧道或平台).</translation>
- </message>
- <message>
- <source>The Homing Bee can be tricky to use. Its turn radius depends on its velocity, so try to not use full power.</source>
- <comment>Tips</comment>
- <translation>使用归巢的蜜蜂有些技巧,它的回转半径和发射速度有关,所以最好不要全力发射。</translation>
- </message>
- <message>
- <source>Downloadable Content</source>
- <translation>可下载内容</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="85"/>
+ <source>Network Game (Play a game across a network)</source>
+ <translation>网络游戏(通过网络)</translation>
</message>
</context>
<context>
<name>PageMultiplayer</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Back</source>
+ <translation type="obsolete">返回</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="309"/>
<source>Start</source>
<translation>开始</translation>
</message>
@@ -900,40 +441,43 @@
<context>
<name>PageNet</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Local</source>
+ <translation type="obsolete">本地</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Internet</source>
+ <translation type="obsolete">Internet</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="520"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
- <source>Please select server from the list above</source>
+ <location filename="../../../../QTfrontend/pages.cpp" line="520"/>
+ <source>Please, select server from the list above</source>
<translation>请选择一个服务器</translation>
</message>
</context>
<context>
<name>PageNetGame</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="626"/>
<source>Control</source>
- <translation>房间管理</translation>
- </message>
- <message>
- <source>Error</source>
- <translation>错误</translation>
- </message>
- <message>
- <source>Please enter room name</source>
- <translation>请键入房间名</translation>
- </message>
- <message>
- <source>OK</source>
- <translation>确定</translation>
+ <translation>Ctrl</translation>
</message>
</context>
<context>
<name>PageNetType</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="1097"/>
<source>LAN game</source>
<translation>局域网游戏</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="1098"/>
<source>Official server</source>
<translation>官方服务器</translation>
</message>
@@ -941,925 +485,681 @@
<context>
<name>PageOptions</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="344"/>
<source>New team</source>
<translation>新队伍</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="345"/>
<source>Edit team</source>
<translation>修改队伍设定</translation>
</message>
<message>
- <source>Delete team</source>
- <translation>删除队伍</translation>
- </message>
- <message>
- <source>New weapon scheme</source>
- <translation type="obsolete">新武器配置</translation>
- </message>
- <message>
- <source>Edit weapon scheme</source>
- <translation type="obsolete">修改武器配置</translation>
- </message>
- <message>
- <source>Delete weapon scheme</source>
- <translation type="obsolete">删除武器配置</translation>
- </message>
- <message>
- <source>You can't edit teams from team selection. Go back to main menu to add, edit or delete teams.</source>
- <translation>您不能在队伍选择界面修改队伍。请返回主页面进行添加、修改、删除队伍等操作。</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Save</source>
+ <translation type="obsolete">保存</translation>
</message>
<message>
- <source>New scheme</source>
- <translation>新框架</translation>
- </message>
- <message>
- <source>Edit scheme</source>
- <translation>修改框架</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Back</source>
+ <translation type="obsolete">返回</translation>
</message>
<message>
- <source>Delete scheme</source>
- <translation>删除框架</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="373"/>
+ <source>Weapons set</source>
+ <translation>新武器设定</translation>
</message>
<message>
- <source>New weapon set</source>
- <translation>新武器配置</translation>
- </message>
- <message>
- <source>Edit weapon set</source>
- <translation>修改武器配置</translation>
- </message>
- <message>
- <source>Delete weapon set</source>
- <translation>删除武器配置</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="376"/>
+ <source>Edit</source>
+ <translation>修改当前武器设定</translation>
</message>
</context>
<context>
<name>PagePlayDemo</name>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="149"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="137"/>
+ <source>Please, select record from the list</source>
+ <translation>请从列表选择记录</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="138"/>
<source>OK</source>
<translation>确认</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="113"/>
<source>Rename dialog</source>
<translation>重命名对话框</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="113"/>
<source>Enter new file name:</source>
<translation>输入新的文件名:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="124"/>
<source>Cannot rename to</source>
<translation>不能改变名字</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="149"/>
<source>Cannot delete file</source>
<translation>不能删除文件</translation>
</message>
- <message>
- <source>Please select record from the list</source>
- <translation>请从列表选择记录</translation>
- </message>
</context>
<context>
<name>PageRoomsList</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="764"/>
<source>Create</source>
<translation>建立</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="765"/>
<source>Join</source>
<translation>加入</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="766"/>
<source>Refresh</source>
<translation>刷新</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="828"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="818"/>
+ <source>Please, enter room name</source>
+ <translation>请键入房间名</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="830"/>
<source>OK</source>
<translation>确认</translation>
</message>
<message>
- <source>Admin features</source>
- <translation>管理员功能</translation>
- </message>
- <message>
- <source>Room Name:</source>
- <translation>房间名:</translation>
- </message>
- <message>
- <source>This game is in lobby.
-You may join and start playing once the game starts.</source>
- <translation>游戏正在大厅中。
-您可以加入等待游戏开始。</translation>
- </message>
- <message>
- <source>This game is in progress.
-You may join and spectate now but you'll have to wait for the game to end to start playing.</source>
- <translation>游戏正在进行中。
-您可以加入观战但必须等游戏结束才能参与游戏。</translation>
- </message>
- <message>
- <source>%1 is the host. He may adjust settings and start the game.</source>
- <translation>%1 是房主,他可以调整设置、开始游戏。</translation>
- </message>
- <message>
- <source>Random Map</source>
- <translation>随机地图</translation>
- </message>
- <message>
- <source>Games may be played on precreated or randomized maps.</source>
- <translation>游戏可以在预先创建或者随机产生的地图上进行。</translation>
- </message>
- <message>
- <source>The Game Scheme defines general options and preferences like Round Time, Sudden Death or Vampirism.</source>
- <translation>游戏设置包括一般选项例如回合时间,突然死亡或吸血模式。</translation>
- </message>
- <message>
- <source>The Weapon Scheme defines available weapons and their ammunition count.</source>
- <translation>武器配置包括可以选用的武器和弹药数量。</translation>
- </message>
- <message numerus="yes">
- <source>There are %1 clients connected to this room.</source>
- <translation>
- <numerusform>有 %1 个客户端连接到这个房间。</numerusform>
- </translation>
- </message>
- <message numerus="yes">
- <source>There are %1 teams participating in this room.</source>
- <translation>
- <numerusform>有 %1 个队伍加入这个房间。</numerusform>
- </translation>
- </message>
- <message>
- <source>Please enter room name</source>
- <translation>请键入房间名</translation>
- </message>
- <message>
- <source>Please select room from the list</source>
+ <location filename="../../../../QTfrontend/pages.cpp" line="829"/>
+ <source>Please, select room from the list</source>
<translation>请从列表选中房间</translation>
</message>
<message>
- <source>Random Maze</source>
- <translation>随机迷宫</translation>
- </message>
- <message>
- <source>State:</source>
- <translation type="obsolete">游戏状态</translation>
- </message>
- <message>
- <source>Rules:</source>
- <translation>规则</translation>
- </message>
- <message>
- <source>Weapons:</source>
- <translation>武器</translation>
- </message>
- <message>
- <source>Search:</source>
- <translation>搜索</translation>
- </message>
- <message>
- <source>Clear</source>
- <translation>清除</translation>
- </message>
- <message>
- <source>Warning</source>
- <translation>警告</translation>
- </message>
- <message>
- <source>The game you are trying to join has started.
-Do you still want to join the room?</source>
- <translation>你要加入的游戏已经开始了。
-还要进入房间吗?</translation>
- </message>
- <message numerus="yes">
- <source>%1 players online</source>
- <translation>
- <numerusform>%1 个玩家在线</numerusform>
- </translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="769"/>
+ <source>Admin features</source>
+ <translation>管理员功能</translation>
</message>
</context>
<context>
<name>PageScheme</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="1008"/>
<source>New</source>
- <translation>新模式</translation>
- </message>
- <message>
- <source>Delete</source>
- <translation>删除</translation>
- </message>
- <message>
- <source>Defend your fort and destroy the opponents, two team colours max!</source>
- <translation>保卫你的城堡,破坏对手的,努力吧!</translation>
- </message>
- <message>
- <source>Teams will start on opposite sides of the terrain, two team colours max!</source>
- <translation>队伍开始在对手的地盘,努力!</translation>
- </message>
- <message>
- <source>Land can not be destroyed!</source>
- <translation>地面无法破坏!</translation>
- </message>
- <message>
- <source>Add an indestructable border around the terrain</source>
- <translation type="obsolete">添加不可毁坏地边界</translation>
- </message>
- <message>
- <source>Lower gravity</source>
- <translation>低重力</translation>
- </message>
- <message>
- <source>Assisted aiming with laser sight</source>
- <translation>激光瞄准辅助</translation>
- </message>
- <message>
- <source>All hogs have a personal forcefield</source>
- <translation>每个刺猬都有一个力场</translation>
- </message>
- <message>
- <source>Enable random mines</source>
- <translation type="obsolete">开启随机地雷</translation>
- </message>
- <message>
- <source>Gain 80% of the damage you do back in health</source>
- <translation>伤害的80%变成自身力量</translation>
- </message>
- <message>
- <source>Share your opponents pain, share their damage</source>
- <translation>分担你的对手的疼痛</translation>
- </message>
- <message>
- <source>Your hogs are unable to move, put your artillery skills to the test</source>
- <translation>你的刺猬不能移动,检验你射击技巧的时候到了</translation>
- </message>
- <message>
- <source>Random</source>
- <translation>随机</translation>
- </message>
- <message>
- <source>Seconds</source>
- <translation>秒钟</translation>
+ <translation>新游戏</translation>
</message>
<message>
- <source>Order of play is random instead of in room order.</source>
- <translation>出场顺序是随机的而不是按照房间顺序。</translation>
- </message>
- <message>
- <source>Play with a King. If he dies, your side dies.</source>
- <translation>国王不能死。</translation>
- </message>
- <message>
- <source>Take turns placing your hedgehogs before the start of play.</source>
- <translation>在开局前轮流手动放置刺猬。</translation>
- </message>
- <message>
- <source>Ammo is shared between all teams that share a colour.</source>
- <translation>同色队伍共享所有弹药。</translation>
- </message>
- <message>
- <source>Disable girders when generating random maps.</source>
- <translation>禁止随机生成地图时使用梁。</translation>
- </message>
- <message>
- <source>Disable land objects when generating random maps.</source>
- <translation type="unfinished">禁止随机生成地图时使用地面物体。</translation>
- </message>
- <message>
- <source>AI respawns on death.</source>
- <translation>AI 死亡再生。</translation>
- </message>
- <message>
- <source>Attacking does not end your turn.</source>
- <translation>攻击不会结束当前回合。</translation>
- </message>
- <message>
- <source>Weapons are reset to starting values each turn.</source>
- <translation>在每回合武器将自动重置到开始设定。</translation>
- </message>
- <message>
- <source>Each hedgehog has its own ammo. It does not share with the team.</source>
- <translation>每个刺猬都有独立的弹药,而非团队分享。</translation>
- </message>
- <message>
- <source>All (living) hedgehogs are fully restored at the end of turn</source>
- <translation>所有活着的刺猬在回合结束时完全恢复健康</translation>
- </message>
- <message>
- <source>You will not have to worry about wind anymore.</source>
- <translation>不用担心风的影响了。</translation>
- </message>
- <message>
- <source>Wind will affect almost everything.</source>
- <translation>风无所不在。</translation>
- </message>
- <message>
- <source>Copy</source>
- <translation>备份</translation>
- </message>
- <message>
- <source>Teams in each clan take successive turns sharing their turn time.</source>
- <translation>在同一集团中的队伍在共用的回合时间里使用连续的回合。</translation>
- </message>
- <message>
- <source>Add an indestructible border around the terrain</source>
- <translation>添加不可毁坏地边界</translation>
- </message>
- <message>
- <source>Add an indestructible border along the bottom</source>
- <translation>在底部添加一个不可毁坏的边界</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="1009"/>
+ <source>Delete</source>
+ <translation>删除</translation>
</message>
</context>
<context>
<name>PageSelectWeapon</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Back</source>
+ <translation type="obsolete">返回</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="729"/>
<source>Default</source>
<translation>默认</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="730"/>
<source>Delete</source>
<translation>删除</translation>
</message>
<message>
- <source>New</source>
- <translation>新模式</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Save</source>
+ <translation type="obsolete">保存</translation>
+ </message>
+</context>
+<context>
+ <name>PageSimpleGame</name>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Back</source>
+ <translation type="obsolete">返回</translation>
</message>
<message>
- <source>Copy</source>
- <translation>备份</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Simple Game</source>
+ <translation type="obsolete">简单游戏</translation>
</message>
</context>
<context>
<name>PageSinglePlayer</name>
<message>
- <source>Simple Game (a quick game against the computer, settings are chosen for you)</source>
- <translation>快速游戏 (使用预设对抗电脑)</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Simple Game</source>
+ <translation type="obsolete">简单游戏</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Training</source>
+ <translation type="obsolete">训练</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Multiplayer</source>
+ <translation type="obsolete">多人游戏</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Saved games</source>
+ <translation type="obsolete">游戏存档</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Demos</source>
+ <translation type="obsolete">Demo</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="685"/>
+ <source>Simple Game (a quick game against the computer, settings are chosen for you)</source>
+ <translation>快速游戏 (对抗电脑,固定设置)</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="688"/>
<source>Multiplayer (play a hotseat game against your friends, or AI teams)</source>
<translation>多人游戏 (热坐对抗朋友或AI)</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="693"/>
<source>Training Mode (Practice your skills in a range of training missions). IN DEVELOPMENT</source>
- <translation type="obsolete">训练模式 (一系列训练任务)。开发中</translation>
+ <translation>训练模式 (一系列训练任务)。开发中</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="699"/>
<source>Demos (Watch recorded demos)</source>
<translation>Demo (观看记录的Demo)</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="702"/>
<source>Load (Load a previously saved game)</source>
<translation>读取 (读取之前保存的游戏)</translation>
</message>
- <message>
- <source>Campaign Mode (...). IN DEVELOPMENT</source>
- <translation type="obsolete">战役模式 ——开发中</translation>
- </message>
- <message>
- <source>Campaign Mode (...)</source>
- <translation>战役模式 (...)</translation>
- </message>
- <message>
- <source>Training Mode (Practice your skills in a range of training missions)</source>
- <translation>训练模式(在一系列训练任务中练习你的技能)</translation>
- </message>
-</context>
-<context>
- <name>PageTraining</name>
- <message>
- <source>No description available</source>
- <translation>没有可用描述</translation>
- </message>
- <message>
- <source>Select a mission!</source>
- <translation>选择一个任务!</translation>
- </message>
</context>
<context>
<name>QAction</name>
<message>
+ <location filename="../../../../QTfrontend/chatwidget.cpp" line="62"/>
<source>Kick</source>
- <translation>踢出</translation>
+ <translation>踢</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="638"/>
<source>Start</source>
<translation>开始</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="628"/>
<source>Restrict Joins</source>
<translation>限制参与</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="630"/>
<source>Restrict Team Additions</source>
- <translation>限制增加团队</translation>
+ <translation>限制团队插件</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/chatwidget.cpp" line="60"/>
<source>Info</source>
<translation>信息</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/chatwidget.cpp" line="64"/>
<source>Ban</source>
<translation>屏蔽</translation>
</message>
- <message>
- <source>Follow</source>
- <translation>跟随</translation>
- </message>
- <message>
- <source>Ignore</source>
- <translation>忽略</translation>
- </message>
- <message>
- <source>Add friend</source>
- <translation>添加朋友</translation>
- </message>
- <message>
- <source>Unignore</source>
- <translation>取消忽略</translation>
- </message>
- <message>
- <source>Remove friend</source>
- <translation>移除朋友</translation>
- </message>
- <message>
- <source>Update</source>
- <translation>更新</translation>
- </message>
</context>
<context>
<name>QCheckBox</name>
<message>
- <source>Check for updates at startup</source>
- <translation>启动时检查程序升级</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="415"/>
+ <source>Enable sound</source>
+ <translation>开启音效</translation>
</message>
<message>
- <source>Enable sound</source>
- <translation>开启游戏音效</translation>
- </message>
- <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="407"/>
<source>Fullscreen</source>
<translation>游戏全屏幕</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="861"/>
+ <source>Forts mode</source>
+ <translation type="obsolete">城堡模式</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="434"/>
<source>Show FPS</source>
<translation>显示帧率 (FPS)</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="438"/>
<source>Alternative damage show</source>
<translation>另一种伤害显示方式</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="419"/>
<source>Enable music</source>
- <translation>开启游戏音乐</translation>
+ <translation>开启音乐</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="411"/>
<source>Frontend fullscreen</source>
- <translation>前端界面全屏幕</translation>
- </message>
- <message>
- <source>Append date and time to record file name</source>
- <translation>记录名称中包含具体时间和日期</translation>
+ <translation>界面全屏幕</translation>
</message>
<message>
- <source>Reduced quality</source>
- <translation type="obsolete">降低显示效果</translation>
- </message>
- <message>
- <source>Show ammo menu tooltips</source>
- <translation>显示武器菜单提示</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="865"/>
+ <source>Divide teams</source>
+ <translation type="obsolete">分组</translation>
</message>
<message>
- <source>Enable frontend sounds</source>
- <translation>开启前端界面音效</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="442"/>
+ <source>Append date and time to record file name</source>
+ <translation>记录名称中包含具体时间日期</translation>
</message>
<message>
- <source>Enable frontend music</source>
- <translation>开启前端界面音乐</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="869"/>
+ <source>Solid land</source>
+ <translation type="obsolete">固实地面</translation>
</message>
<message>
- <source>Frontend effects</source>
- <translation>前端界面效果</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="403"/>
+ <source>Reduce Quality</source>
+ <translation>降低质量</translation>
</message>
</context>
<context>
<name>QComboBox</name>
<message>
+ <location filename="../../../../QTfrontend/mapContainer.cpp" line="62"/>
<source>generated map...</source>
<translation>生成地图...</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="171"/>
<source>Human</source>
<translation>玩家</translation>
</message>
<message>
- <source>Level</source>
- <translation>Lv 级别</translation>
- </message>
- <message>
- <source>(System default)</source>
- <translation>(系统默认)</translation>
- </message>
- <message>
- <source>generated maze...</source>
- <translation>生成迷宫...</translation>
- </message>
- <message>
- <source>Mission</source>
- <translation>任务</translation>
- </message>
- <message>
- <source>Community</source>
- <translation>社区</translation>
- </message>
- <message>
- <source>Any</source>
- <translation>任意</translation>
- </message>
- <message>
- <source>In lobby</source>
- <translation>大厅中</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Level 5</source>
+ <translation type="obsolete">Lv 5</translation>
</message>
<message>
- <source>In progress</source>
- <translation>进行中</translation>
- </message>
- <message>
- <source>Default</source>
- <translation type="obsolete">默认</translation>
- </message>
- <message>
- <source>Pro mode</source>
- <translation type="obsolete">高手模式</translation>
- </message>
- <message>
- <source>Shoppa</source>
- <translation type="obsolete">绳子党</translation>
- </message>
- <message>
- <source>Basketball</source>
- <translation type="obsolete">篮球</translation>
- </message>
- <message>
- <source>Minefield</source>
- <translation type="obsolete">雷区</translation>
- </message>
- <message>
- <source>Barrel mayhem</source>
- <translation type="obsolete">炼狱场</translation>
- </message>
- <message>
- <source>Tunnel hogs</source>
- <translation type="obsolete">刺猬洞</translation>
- </message>
- <message>
- <source>Crazy</source>
- <translation type="obsolete">疯狂刺猬</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Level 4</source>
+ <translation type="obsolete">Lv 4</translation>
</message>
<message>
- <source>hand drawn map...</source>
- <translation>手绘地图...</translation>
- </message>
- <message>
- <source>Disabled</source>
- <translation>禁用</translation>
- </message>
- <message>
- <source>Red/Cyan</source>
- <translation>红/青</translation>
- </message>
- <message>
- <source>Cyan/Red</source>
- <translation>青/红</translation>
- </message>
- <message>
- <source>Red/Blue</source>
- <translation>红/蓝</translation>
- </message>
- <message>
- <source>Blue/Red</source>
- <translation>蓝/红</translation>
- </message>
- <message>
- <source>Red/Green</source>
- <translation>红/绿</translation>
- </message>
- <message>
- <source>Green/Red</source>
- <translation>绿/红</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Level 3</source>
+ <translation type="obsolete">Lv 3</translation>
</message>
<message>
- <source>Side-by-side</source>
- <translation>横向排列</translation>
- </message>
- <message>
- <source>Top-Bottom</source>
- <translation>自顶向下</translation>
- </message>
- <message>
- <source>Wiggle</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <source>Red/Cyan grayscale</source>
- <translation type="unfinished">红/青 灰度</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Level 2</source>
+ <translation type="obsolete">Lv 2</translation>
</message>
<message>
- <source>Cyan/Red grayscale</source>
- <translation type="unfinished">青/红 灰度</translation>
- </message>
- <message>
- <source>Red/Blue grayscale</source>
- <translation type="unfinished">红/蓝 灰度</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Level 1</source>
+ <translation type="obsolete">Lv 1</translation>
</message>
<message>
- <source>Blue/Red grayscale</source>
- <translation type="unfinished">蓝/红 灰度</translation>
- </message>
- <message>
- <source>Red/Green grayscale</source>
- <translation type="unfinished">红/绿 灰度</translation>
- </message>
- <message>
- <source>Green/Red grayscale</source>
- <translation type="unfinished">绿/红 灰度</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="175"/>
+ <source>Level</source>
+ <translation>Lv 级别</translation>
</message>
</context>
<context>
<name>QGroupBox</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="126"/>
<source>Team Members</source>
<translation>成员</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="161"/>
+ <source>Team</source>
+ <translation>队伍</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="202"/>
<source>Fort</source>
<translation>城堡模式</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="236"/>
<source>Key binds</source>
<translation>键位绑定</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="335"/>
<source>Teams</source>
<translation>队伍</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="384"/>
<source>Audio/Graphic options</source>
<translation>音频/视频选项</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Net nick</source>
+ <translation type="obsolete">昵称</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Net options</source>
+ <translation type="obsolete">网络选项</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Landscape</source>
+ <translation type="obsolete">地形</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Game scheme</source>
+ <translation type="obsolete">游戏设置</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="228"/>
<source>Playing teams</source>
<translation>玩家队伍</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+ <source>Team level</source>
+ <translation type="obsolete">队伍级别</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="473"/>
<source>Net game</source>
- <translation>局域网络游戏</translation>
+ <translation>网络游戏</translation>
</message>
<message>
- <source>Weapons</source>
- <translation type="obsolete">武器</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Servers list</source>
+ <translation type="obsolete">服务器列表</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="370"/>
+ <source>Weapons</source>
+ <translation>武器</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="862"/>
+ <source>Scheme options</source>
+ <translation type="obsolete">游戏设定</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="866"/>
<source>Game Modifiers</source>
<translation>游戏修改</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="867"/>
<source>Basic Settings</source>
<translation>基本设置</translation>
</message>
- <message>
- <source>Team Settings</source>
- <translation>队伍设定</translation>
- </message>
- <message>
- <source>Misc</source>
- <translation>杂项</translation>
- </message>
- <message>
- <source>Schemes and Weapons</source>
- <translation>游戏框架和武器配置</translation>
- </message>
</context>
<context>
<name>QLabel</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="353"/>
<source>Net nick</source>
<translation>网络游戏昵称</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Server address</source>
+ <translation type="obsolete">服务器地址</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source><div align="center"><h1>Hedgewars</h1><h3>Version 0.8</h3><p><a href="http://www.hedgewars.org/">http://www.hedgewars.org/</a></p><br>This program is distributed under the GNU General Public License</div></source>
+ <translation type="obsolete"><div align="center"><h1>刺猬大作战</h1><h3>0.8</h3><p><a href="http://www.hedgewars.org/">http://www.hedgewars.org/</a></p><br>This program is distributed under the GNU General Public License</div></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source><h2>Developers:</h2><p>Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;<br>Igor Ulyanov &lt;<a href="mailto:iulyanov@gmail.com">iulyanov@gmail.com</a>&gt;</p><h2>Translations:</h2>english: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;<br>russian: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;</source>
+ <translation type="obsolete"><h2>Developers:</h2><p>Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;<br>Igor Ulyanov &lt;<a href="mailto:iulyanov@gmail.com">iulyanov@gmail.com</a>&gt;</p><h2>Translations:</h2>english: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;<br>russian: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>difficulty:</source>
+ <translation type="obsolete">难度:</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source><h3>Version 0.8</h3></source>
+ <translation type="obsolete"><h3>版本 0.8</h3></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="46"/>
<source>This program is distributed under the GNU General Public License</source>
- <translation type="obsolete">This program is distributed under the GNU General Public License</translation>
+ <translation>This program is distributed under the GNU General Public License</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+ <source><h2>Translations:</h2></source>
+ <translation type="obsolete"><h2>翻译:</h2></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+ <source><h2>Developers:</h2></source>
+ <translation type="obsolete"><h2>开发者:</h2></translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+ <source><h2>Translations:</h2><p></source>
+ <translation type="obsolete"><h2>翻译:</h2><p></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+ <source><h2>Special thanks:</h2><p></source>
+ <translation type="obsolete"><h2>特别感谢:</h2><p></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+ <source><h3>Version 0.8.1</h3></source>
+ <translation type="obsolete"><h3>版本 0.8.1</h3></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="0"/>
+ <source><h2></h2><p></p></source>
+ <translation type="obsolete"><h2></h2><p></p></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="62"/>
+ <source>Turn time</source>
+ <translation type="obsolete">回合时间</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="63"/>
+ <source>Initial health</source>
+ <translation type="obsolete">初始生命值</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="687"/>
+ <source><p>The best shot award was won by <b>%1</b> with <b>%2</b> pts.</p></source>
+ <translation type="obsolete"><p>射击冠军<b>%1</b> with <b>%2</b> .</p></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="699"/>
+ <source><p>A total of <b>%1</b> Hedgehog(s) were killed during this round.</p></source>
+ <translation type="obsolete"><p>阵亡<b>%1</b> </p></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="0"/>
+ <source><h3>Version 0.9</h3></source>
+ <translation type="obsolete"><h3>版本0.9</h3></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="389"/>
<source>Resolution</source>
<translation>分辨率</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="398"/>
<source>FPS limit</source>
<translation>FPS 上限</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/about.cpp" line="61"/>
<source>Developers:</source>
<translation>开发者:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/about.cpp" line="71"/>
<source>Art:</source>
<translation>艺术:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/about.cpp" line="91"/>
<source>Translations:</source>
<translation>翻译:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/about.cpp" line="111"/>
<source>Special thanks:</source>
<translation>特别感谢:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="564"/>
<source>Server name:</source>
<translation>服务器名:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="571"/>
<source>Server port:</source>
<translation>服务器端口:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/input_ip.cpp" line="32"/>
<source>Host:</source>
<translation>主机:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/input_ip.cpp" line="36"/>
<source>Port:</source>
<translation>端口:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="61"/>
<source>Weapons</source>
<translation>武器</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="0"/>
+ <source><h3>Version 0.9.2</h3></source>
+ <translation type="obsolete"><h3>版本0.9.2</h3></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="43"/>
<source>Version</source>
<translation>版本</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="694"/>
+ <source><p>The best shot award was won by <b>%1</b> with <b>%2</b> kills.</p></source>
+ <translation type="obsolete"><p>最佳射手<b>%1</b>取得的战果 <b>%2</b></p></translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/about.cpp" line="83"/>
<source>Sounds:</source>
<translation>声音:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="424"/>
<source>Initial sound volume</source>
<translation>初始音量</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="923"/>
<source>Damage Modifier</source>
- <translation>伤害修正值</translation>
+ <translation>伤害修改</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="938"/>
<source>Turn Time</source>
<translation>回合时间</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="953"/>
<source>Initial Health</source>
<translation>初始生命值</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="968"/>
<source>Sudden Death Timeout</source>
<translation>死亡模式倒计时</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="980"/>
+ <source>Case Probability</source>
+ <translation type="obsolete">箱子掉落几率</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="998"/>
<source>Scheme Name:</source>
<translation>设置名称:</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="983"/>
<source>Crate Drops</source>
<translation>箱子降落</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/gamecfgwidget.cpp" line="53"/>
<source>Game scheme</source>
<translation>游戏设置</translation>
</message>
- <message>
- <source>Mines Time</source>
- <translation>布雷时间</translation>
- </message>
- <message>
- <source>Mines</source>
- <translation>地雷</translation>
- </message>
- <message>
- <source>% Dud Mines</source>
- <translation>% 地雷故障</translation>
- </message>
- <message>
- <source>Name</source>
- <translation>名称</translation>
- </message>
- <message>
- <source>Type</source>
- <translation>类型</translation>
- </message>
- <message>
- <source>Grave</source>
- <translation>墓碑</translation>
- </message>
- <message>
- <source>Flag</source>
- <translation>旗帜</translation>
- </message>
- <message>
- <source>Voice</source>
- <translation>声音</translation>
- </message>
- <message>
- <source>Locale</source>
- <translation>Locale</translation>
- </message>
- <message>
- <source>Restart game to apply</source>
- <translation>需要重新启动游戏方可应用</translation>
- </message>
- <message>
- <source>Explosives</source>
- <translation>爆炸物</translation>
- </message>
- <message>
- <source>Tip: </source>
- <translation>提示:</translation>
- </message>
- <message>
- <source>This development build is 'work in progress' and may not be compatible with other versions of the game. Some features might be broken or incomplete. Use at your own risk!</source>
- <translation>当前运行的为开发版本,不同其他版本兼容。功能或许损坏、不完整。请自行承担风险!</translation>
- </message>
- <message>
- <source>Quality</source>
- <translation>图像质量</translation>
- </message>
- <message>
- <source>Sudden Death Water Rise</source>
- <translation>死亡模式水位上涨</translation>
- </message>
- <message>
- <source>Sudden Death Health Decrease</source>
- <translation>死亡模式健康降低</translation>
- </message>
- <message>
- <source>% Rope Length</source>
- <translation>% 绳长</translation>
- </message>
- <message>
- <source>% Health Crates</source>
- <translation>% 生命箱</translation>
- </message>
- <message>
- <source>Health in Crates</source>
- <translation>生命箱的值数</translation>
- </message>
- <message>
- <source>Gameplay</source>
- <translation type="obsolete">游戏</translation>
- </message>
- <message>
- <source>Stereo rendering</source>
- <translation>立体渲染</translation>
- </message>
- <message>
- <source>Style</source>
- <translation type="unfinished">样式</translation>
- </message>
- <message>
- <source>Scheme</source>
- <translation type="unfinished">模式</translation>
- </message>
- <message>
- <source>Password</source>
- <translation>密码</translation>
- </message>
- <message>
- <source>% Get Away Time</source>
- <translation>% 脱身时间</translation>
- </message>
- <message>
- <source>This program is distributed under the GNU General Public License v2</source>
- <translation>本程序在GNU通用许可证协议第二版(GNU GPLv2)下发布</translation>
- </message>
</context>
<context>
<name>QLineEdit</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="358"/>
<source>unnamed</source>
<translation>无名</translation>
</message>
- <message>
- <source>hedgehog %1</source>
- <translation>刺猬 %1</translation>
- </message>
</context>
<context>
<name>QMainWindow</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>-= by unC0Rr =-</source>
+ <translation type="obsolete">-= by unC0Rr =-</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Hedgewars</source>
+ <translation type="obsolete">刺猬大作战</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/ui_hwform.cpp" line="37"/>
<source>Hedgewars %1</source>
<translation>刺猬大作战 %1</translation>
</message>
@@ -1867,10 +1167,12 @@
<context>
<name>QMessageBox</name>
<message>
+ <location filename="../../../../QTfrontend/main.cpp" line="272"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/main.cpp" line="275"/>
<source>Failed to open data directory:
%1
Please check your installation</source>
@@ -1879,233 +1181,292 @@
请检查</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="685"/>
<source>Network</source>
<translation>网络</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/hwform.cpp" line="686"/>
<source>Connection to server is lost</source>
- <translation>与服务器的连接丢失</translation>
+ <translation>服务器连接丢失</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/selectWeapon.cpp" line="170"/>
<source>Weapons</source>
<translation>武器</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/selectWeapon.cpp" line="165"/>
<source>Can not delete default weapon set</source>
- <translation type="obsolete">不能删除默认武器设定</translation>
+ <translation>不能删除默认武器设定</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/selectWeapon.cpp" line="170"/>
<source>Really delete this weapon set?</source>
<translation>真的删除这个武器设定吗?</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/selectWeapon.cpp" line="127"/>
<source>Can not edit default weapon set</source>
- <translation type="obsolete">不能更改默认的武器设定</translation>
- </message>
- <message>
- <source>Can not overwrite default weapon set '%1'!</source>
- <translation>不能覆盖默认的武器配置 '%1'!</translation>
- </message>
- <message>
- <source>All file associations have been set.</source>
- <translation>所有相关文件已经设定。</translation>
- </message>
- <message>
- <source>Teams</source>
- <translation>队伍</translation>
- </message>
- <message>
- <source>Really delete this team?</source>
- <translation>真的要删除队伍?</translation>
- </message>
- <message>
- <source>Schemes</source>
- <translation>游戏框架</translation>
- </message>
- <message>
- <source>Can not delete default scheme '%1'!</source>
- <translation>无法删除默认游戏框架 '%1'!</translation>
- </message>
- <message>
- <source>File association failed.</source>
- <translation>文件关联失败。</translation>
- </message>
- <message>
- <source>Really delete this game scheme?</source>
- <translation>真的删除此游戏框架?</translation>
- </message>
- <message>
- <source>Can not delete default weapon set '%1'!</source>
- <translation>无法删除武器配置%1'!</translation>
+ <translation>不能更改默认的武器设定</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
+ <location filename="../../../../QTfrontend/main.cpp" line="35"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/main.cpp" line="36"/>
<source>Cannot create directory %1</source>
<translation>无法创建路径 %1</translation>
</message>
<message>
- <source>OK</source>
- <translation>确认</translation>
+ <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+ <source>Quit</source>
+ <translation type="obsolete">退出</translation>
</message>
<message>
- <source>Nickname</source>
- <translation>昵称</translation>
- </message>
- <message>
- <source>Please enter your nickname</source>
- <translation>请输入您的昵称</translation>
+ <location filename="../../../../QTfrontend/main.cpp" line="37"/>
+ <source>OK</source>
+ <translation>确认</translation>
</message>
</context>
<context>
<name>QPushButton</name>
<message>
+ <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+ <source>Single Player</source>
+ <translation type="obsolete">单人游戏</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+ <source>Multiplayer</source>
+ <translation type="obsolete">多人游戏</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+ <source>Net game</source>
+ <translation type="obsolete">网络游戏</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/main.cpp" line="0"/>
+ <source>Demos</source>
+ <translation type="obsolete">Demo</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="237"/>
<source>Setup</source>
<translation>设置</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+ <source>Exit</source>
+ <translation type="obsolete">退出</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+ <source>Back</source>
+ <translation type="obsolete">返回</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+ <source>Simple Game</source>
+ <translation type="obsolete">简单游戏</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+ <source>Discard</source>
+ <translation type="obsolete">中止</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/teamselect.cpp" line="0"/>
+ <source>Save</source>
+ <translation type="obsolete">保存</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="75"/>
<source>Play demo</source>
<translation>播放 demo</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+ <source>New team</source>
+ <translation type="obsolete">新队伍</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+ <source>Edit team</source>
+ <translation type="obsolete">编辑队伍</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="482"/>
<source>Connect</source>
<translation>连接</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Disconnect</source>
+ <translation type="obsolete">失去连接</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Join</source>
+ <translation type="obsolete">加入</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Create</source>
+ <translation type="obsolete">创建</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Add Team</source>
+ <translation type="obsolete">添加队伍</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="714"/>
<source>Go!</source>
<translation>上场!</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="544"/>
<source>Start</source>
<translation>开始</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>About</source>
+ <translation type="obsolete">关于</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="466"/>
<source>Start server</source>
<translation>开始服务端</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="491"/>
<source>Update</source>
<translation>更新</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="0"/>
+ <source>Waiting</source>
+ <translation type="obsolete">等待中</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="80"/>
<source>Load</source>
<translation>读取</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+ <source>Weapons scheme</source>
+ <translation type="obsolete">武器设定</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="0"/>
+ <source>Training</source>
+ <translation type="obsolete">训练</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="496"/>
<source>Specify</source>
<translation>指定</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="580"/>
<source>default</source>
<translation>默认</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="48"/>
<source>Rename</source>
<translation>重命名</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/input_ip.cpp" line="52"/>
<source>OK</source>
<translation>确定</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/input_ip.cpp" line="57"/>
<source>Cancel</source>
<translation>取消</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/playrecordpage.cpp" line="52"/>
<source>Delete</source>
<translation>删除</translation>
</message>
<message>
- <source>Ready</source>
- <translation>准备好了</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="501"/>
+ <source>Join official server</source>
+ <translation type="obsolete">加入官方服务器</translation>
</message>
<message>
- <source>Random Team</source>
- <translation>随机分配队伍</translation>
- </message>
- <message>
- <source>Associate file extensions</source>
- <translation>相关文件扩展</translation>
- </message>
- <message>
- <source>more</source>
- <translation>更多</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="618"/>
+ <source>Ready</source>
+ <translation>准备好了</translation>
</message>
</context>
<context>
<name>QTableWidget</name>
<message>
- <source>Room Name</source>
- <translation>房间名</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="787"/>
+ <source>Room name</source>
+ <translation>房间名称</translation>
</message>
<message>
- <source>C</source>
- <translation>人数限制</translation>
- </message>
- <message>
- <source>T</source>
- <translation>时间限制</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="788"/>
+ <source>Players number</source>
+ <translation>玩家数量</translation>
</message>
<message>
- <source>Owner</source>
- <translation>创建者</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="790"/>
+ <source>Round in progress</source>
+ <translation>回合数</translation>
+ </message>
+</context>
+<context>
+ <name>QToolBox</name>
+ <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="242"/>
+ <source>Actions</source>
+ <translation>行动</translation>
</message>
<message>
- <source>Map</source>
- <translation>地图</translation>
- </message>
- <message>
- <source>Rules</source>
- <translation>规则</translation>
- </message>
- <message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="244"/>
<source>Weapons</source>
<translation>武器</translation>
</message>
-</context>
-<context>
- <name>SelWeaponWidget</name>
<message>
- <source>Weapon set</source>
- <translation>武器设置</translation>
- </message>
- <message>
- <source>Probabilities</source>
- <translation>空中支援几率</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="246"/>
+ <source>Weapon properties</source>
+ <translation>武器选项</translation>
</message>
<message>
- <source>Ammo in boxes</source>
- <translation>弹药箱</translation>
- </message>
- <message>
- <source>Delays</source>
- <translation>延迟回合数</translation>
- </message>
- <message>
- <source>new</source>
- <translation>新</translation>
- </message>
- <message>
- <source>copy of</source>
- <translation>副本</translation>
+ <location filename="../../../../QTfrontend/pages.cpp" line="248"/>
+ <source>Other</source>
+ <translation>其他</translation>
</message>
</context>
<context>
<name>TCPBase</name>
<message>
+ <location filename="../../../../QTfrontend/tcpBase.cpp" line="99"/>
<source>Error</source>
<translation>错误</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/tcpBase.cpp" line="45"/>
<source>Unable to start the server: %1.</source>
<translation>无法开始服务端: %1.</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/tcpBase.cpp" line="101"/>
<source>Unable to run engine: %1 (</source>
<translation>无法运行引擎: %1 (</translation>
</message>
@@ -2113,678 +1474,362 @@
<context>
<name>ToggleButtonWidget</name>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="888"/>
<source>Fort Mode</source>
<translation>城堡模式</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="892"/>
<source>Divide Teams</source>
<translation>团体行动</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="896"/>
<source>Solid Land</source>
<translation>固实地面</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="900"/>
<source>Add Border</source>
<translation>添加边界</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="904"/>
<source>Low Gravity</source>
<translation>低重力</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="908"/>
<source>Laser Sight</source>
<translation>激光瞄准</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="912"/>
<source>Invulnerable</source>
<translation>刀枪不入</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/pages.cpp" line="916"/>
<source>Add Mines</source>
- <translation type="obsolete">布置地雷</translation>
- </message>
- <message>
- <source>Vampirism</source>
- <translation>吸血鬼</translation>
- </message>
- <message>
- <source>Karma</source>
- <translation>因果报应</translation>
- </message>
- <message>
- <source>Artillery</source>
- <translation>射术</translation>
- </message>
- <message>
- <source>Random Order</source>
- <translation>随机顺序</translation>
- </message>
- <message>
- <source>King</source>
- <translation>国王模式</translation>
- </message>
- <message>
- <source>Place Hedgehogs</source>
- <translation>手动放置刺猬</translation>
- </message>
- <message>
- <source>Clan Shares Ammo</source>
- <translation>团队共享弹药</translation>
- </message>
- <message>
- <source>Disable Girders</source>
- <translation>禁止梁</translation>
- </message>
- <message>
- <source>Disable Land Objects</source>
- <translation>禁止地面物件</translation>
- </message>
- <message>
- <source>AI Survival Mode</source>
- <translation>AI生存模式</translation>
- </message>
- <message>
- <source>Unlimited Attacks</source>
- <translation>无限攻击</translation>
- </message>
- <message>
- <source>Reset Weapons</source>
- <translation>重置武器</translation>
- </message>
- <message>
- <source>Per Hedgehog Ammo</source>
- <translation>每个刺猬的弹药</translation>
- </message>
- <message>
- <source>Reset Health</source>
- <translation>重置生命值</translation>
- </message>
- <message>
- <source>Disable Wind</source>
- <translation>禁止风力作用</translation>
- </message>
- <message>
- <source>More Wind</source>
- <translation>让风来地更猛烈吧</translation>
- </message>
- <message>
- <source>Tag Team</source>
- <translation>为队伍添加标签</translation>
- </message>
- <message>
- <source>Add Bottom Border</source>
- <translation>添加底部边界</translation>
+ <translation>布置地雷</translation>
</message>
</context>
<context>
<name>binds</name>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="23"/>
<source>up</source>
<translation>上</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="24"/>
<source>left</source>
<translation>左</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="25"/>
<source>right</source>
<translation>右</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="26"/>
<source>down</source>
<translation>下</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="28"/>
+ <source>jump</source>
+ <translation>跳</translation>
+ </message>
+ <message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="29"/>
<source>attack</source>
<translation>攻击</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="31"/>
<source>put</source>
<translation>放</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="32"/>
<source>switch</source>
<translation>切换</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="35"/>
<source>slot 1</source>
<translation>slot 1</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="36"/>
<source>slot 2</source>
<translation>slot 2</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="37"/>
<source>slot 3</source>
<translation>slot 3</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="38"/>
<source>slot 4</source>
<translation>slot 4</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="39"/>
<source>slot 5</source>
<translation>slot 5</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="40"/>
<source>slot 6</source>
<translation>slot 6</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="41"/>
<source>slot 7</source>
<translation>slot 7</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="42"/>
<source>slot 8</source>
<translation>slot 8</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="44"/>
<source>timer 1 sec</source>
<translation>定时1秒</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="45"/>
<source>timer 2 sec</source>
<translation>定时2秒</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="46"/>
<source>timer 3 sec</source>
<translation>定时3秒</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="47"/>
<source>timer 4 sec</source>
<translation>定时4秒</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="48"/>
<source>timer 5 sec</source>
<translation>定时5秒</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="56"/>
<source>capture</source>
- <translation>截取</translation>
+ <translation>夺取</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="58"/>
<source>quit</source>
<translation>退出</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="33"/>
<source>find hedgehog</source>
- <translation>寻找刺猬</translation>
+ <translation>找到 刺猬</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="34"/>
<source>ammo menu</source>
<translation>弹药菜单</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="53"/>
<source>volume down</source>
<translation>降低音量</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="54"/>
<source>volume up</source>
<translation>提高音量</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="55"/>
<source>change mode</source>
<translation>改变模式</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="51"/>
<source>pause</source>
<translation>暂停</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="43"/>
<source>slot 9</source>
<translation>slot 9</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="57"/>
<source>hedgehogs
info</source>
<translation>刺猬大作战
信息</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="49"/>
<source>chat</source>
<translation>聊天</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="50"/>
<source>chat history</source>
<translation>聊天记录</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="52"/>
<source>confirmation</source>
<translation>确认</translation>
</message>
<message>
+ <location filename="../../../../QTfrontend/binds.cpp" line="30"/>
<source>precise aim</source>
<translation>练习瞄准</translation>
</message>
- <message>
- <source>zoom in</source>
- <translation>放大</translation>
- </message>
- <message>
- <source>zoom out</source>
- <translation>缩小</translation>
- </message>
- <message>
- <source>reset zoom</source>
- <translation>重置</translation>
- </message>
- <message>
- <source>long jump</source>
- <translation>远跳</translation>
- </message>
- <message>
- <source>high jump</source>
- <translation>高跳</translation>
- </message>
- <message>
- <source>slot 10</source>
- <translation>slot 10</translation>
- </message>
-</context>
-<context>
- <name>binds (categories)</name>
- <message>
- <source>Basic controls</source>
- <translation>基本控制</translation>
- </message>
- <message>
- <source>Weapon controls</source>
- <translation>武器控制</translation>
- </message>
- <message>
- <source>Camera and cursor controls</source>
- <translation>镜头和光标控制</translation>
- </message>
- <message>
- <source>Other</source>
- <translation>其他</translation>
- </message>
-</context>
-<context>
- <name>binds (descriptions)</name>
- <message>
- <source>Move your hogs and aim:</source>
- <translation>移动您的刺猬同时瞄准:</translation>
- </message>
- <message>
- <source>Traverse gaps and obstacles by jumping:</source>
- <translation>使用跳跃越过沟渠、障碍:</translation>
- </message>
- <message>
- <source>Fire your selected weapon or trigger an utility item:</source>
- <translation>使用选择的武器开火、使用物品:</translation>
- </message>
- <message>
- <source>Pick a weapon or a target location under the cursor:</source>
- <translation>选取一个武器或者瞄准光标下的地点:</translation>
- </message>
- <message>
- <source>Switch your currently active hog (if possible):</source>
- <translation>切换到您当前活动的刺猬(如果可用):</translation>
- </message>
- <message>
- <source>Pick a weapon or utility item:</source>
- <translation>选择一个武器或物品:</translation>
- </message>
- <message>
- <source>Set the timer on bombs and timed weapons:</source>
- <translation>设置定时炸弹等武器起爆时间:</translation>
- </message>
- <message>
- <source>Move the camera to the active hog:</source>
- <translation>移动镜头到选中的刺猬:</translation>
- </message>
- <message>
- <source>Move the cursor or camera without using the mouse:</source>
- <translation>不用鼠标移动光标或镜头:</translation>
- </message>
- <message>
- <source>Modify the camera's zoom level:</source>
- <translation>调整镜头放大倍数:</translation>
- </message>
- <message>
- <source>Talk to your team or all participants:</source>
- <translation>同队友或全部参与者对话:</translation>
- </message>
- <message>
- <source>Pause, continue or leave your game:</source>
- <translation>暂停、继续或离开游戏:</translation>
- </message>
- <message>
- <source>Modify the game's volume while playing:</source>
- <translation>调整游戏时音量:</translation>
- </message>
- <message>
- <source>Toggle fullscreen mode:</source>
- <translation>全屏模式:</translation>
- </message>
- <message>
- <source>Take a screenshot:</source>
- <translation>截图:</translation>
- </message>
- <message>
- <source>Toggle labels above hedgehogs:</source>
- <translation>切换刺猬头顶标签的显示方式:</translation>
- </message>
</context>
<context>
- <name>binds (keys)</name>
- <message>
- <source>Axis</source>
- <translation>Axis轴</translation>
- </message>
+ <name>teams</name>
<message>
- <source>(Up)</source>
- <translation>(上)</translation>
- </message>
- <message>
- <source>(Down)</source>
- <translation>(下)</translation>
- </message>
- <message>
- <source>Hat</source>
- <translation>帽子</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="53"/>
+ <source>Hedgehogs</source>
+ <translation>刺猬 </translation>
</message>
<message>
- <source>(Left)</source>
- <translation>(左)</translation>
- </message>
- <message>
- <source>(Right)</source>
- <translation>(右)</translation>
- </message>
- <message>
- <source>Button</source>
- <translation>按键</translation>
- </message>
- <message>
- <source>Keyboard</source>
- <translation>键盘</translation>
- </message>
- <message>
- <source>Mouse: Left button</source>
- <translation>鼠标:左键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="54"/>
+ <source>hedgehog 1</source>
+ <translation>刺猬 1号</translation>
</message>
<message>
- <source>Mouse: Middle button</source>
- <translation>鼠标:中键</translation>
- </message>
- <message>
- <source>Mouse: Right button</source>
- <translation>鼠标:右键</translation>
- </message>
- <message>
- <source>Mouse: Wheel up</source>
- <translation>鼠标滚轮:向上</translation>
- </message>
- <message>
- <source>Mouse: Wheel down</source>
- <translation>鼠标滚轮:向下</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="55"/>
+ <source>hedgehog 2</source>
+ <translation>刺猬 2号</translation>
</message>
<message>
- <source>Backspace</source>
- <translation>退格键</translation>
- </message>
- <message>
- <source>Tab</source>
- <translation>Tab</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="56"/>
+ <source>hedgehog 3</source>
+ <translation>刺猬 3号</translation>
</message>
<message>
- <source>Clear</source>
- <translation>Num Lock / Clear</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="57"/>
+ <source>hedgehog 4</source>
+ <translation>刺猬 4号</translation>
</message>
<message>
- <source>Return</source>
- <translation>回车</translation>
- </message>
- <message>
- <source>Pause</source>
- <translation>暂停键</translation>
- </message>
- <message>
- <source>Escape</source>
- <translation>ESC键(退出键)</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="58"/>
+ <source>hedgehog 5</source>
+ <translation>刺猬 5号</translation>
</message>
<message>
- <source>Space</source>
- <translation>空格键</translation>
- </message>
- <message>
- <source>Delete</source>
- <translation>Del(删除键)</translation>
- </message>
- <message>
- <source>Numpad 0</source>
- <translation>小键盘0</translation>
- </message>
- <message>
- <source>Numpad 1</source>
- <translation>小键盘1</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="59"/>
+ <source>hedgehog 6</source>
+ <translation>刺猬 6号</translation>
</message>
<message>
- <source>Numpad 2</source>
- <translation>小键盘2</translation>
- </message>
- <message>
- <source>Numpad 3</source>
- <translation>小键盘3</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="60"/>
+ <source>hedgehog 7</source>
+ <translation>刺猬 7号</translation>
</message>
<message>
- <source>Numpad 4</source>
- <translation>小键盘4</translation>
- </message>
- <message>
- <source>Numpad 5</source>
- <translation>小键盘5</translation>
- </message>
- <message>
- <source>Numpad 6</source>
- <translation>小键盘6</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="61"/>
+ <source>hedgehog 8</source>
+ <translation>刺猬 8号</translation>
</message>
<message>
- <source>Numpad 7</source>
- <translation>小键盘7</translation>
- </message>
- <message>
- <source>Numpad 8</source>
- <translation>小键盘8</translation>
- </message>
- <message>
- <source>Numpad 9</source>
- <translation>小键盘9</translation>
- </message>
- <message>
- <source>Numpad .</source>
- <translation>小键盘.</translation>
- </message>
- <message>
- <source>Numpad /</source>
- <translation>小键盘/</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="73"/>
+ <source>Goddess</source>
+ <translation>女神</translation>
</message>
<message>
- <source>Numpad *</source>
- <translation>小键盘*</translation>
- </message>
- <message>
- <source>Numpad -</source>
- <translation>小键盘-</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="74"/>
+ <source>Isis</source>
+ <translation>艾希丝</translation>
</message>
<message>
- <source>Numpad +</source>
- <translation>小键盘+</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="75"/>
+ <source>Astarte</source>
+ <translation>阿斯德尔特</translation>
</message>
<message>
- <source>Enter</source>
- <translation>回车键</translation>
- </message>
- <message>
- <source>Equals</source>
- <translation>等于</translation>
- </message>
- <message>
- <source>Up</source>
- <translation>上</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="76"/>
+ <source>Diana</source>
+ <translation>黛安娜</translation>
</message>
<message>
- <source>Down</source>
- <translation>下</translation>
- </message>
- <message>
- <source>Right</source>
- <translation>右</translation>
- </message>
- <message>
- <source>Left</source>
- <translation>左</translation>
- </message>
- <message>
- <source>Insert</source>
- <translation>插入键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="77"/>
+ <source>Aphrodite</source>
+ <translation>阿弗罗狄特</translation>
</message>
<message>
- <source>Home</source>
- <translation>Home键</translation>
- </message>
- <message>
- <source>End</source>
- <translation>End键</translation>
- </message>
- <message>
- <source>Page up</source>
- <translation>向上翻页键</translation>
- </message>
- <message>
- <source>Page down</source>
- <translation>向下翻页键</translation>
- </message>
- <message>
- <source>Num lock</source>
- <translation>小键盘数字锁</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="78"/>
+ <source>Hecate</source>
+ <translation>赫卡特</translation>
</message>
<message>
- <source>Caps lock</source>
- <translation>大小写切换键</translation>
- </message>
- <message>
- <source>Scroll lock</source>
- <translation>Scroll Lock键</translation>
- </message>
- <message>
- <source>Right shift</source>
- <translation>右Shift键</translation>
- </message>
- <message>
- <source>Left shift</source>
- <translation>左Shift键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="79"/>
+ <source>Demeter</source>
+ <translation>得墨忒耳</translation>
</message>
<message>
- <source>Right ctrl</source>
- <translation>右Ctrl键</translation>
- </message>
- <message>
- <source>Left ctrl</source>
- <translation>左Ctrl键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="80"/>
+ <source>Kali</source>
+ <translation>迦梨</translation>
</message>
<message>
- <source>Right alt</source>
- <translation>右Alt键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="81"/>
+ <source>Inanna</source>
+ <translation>維納斯</translation>
</message>
<message>
- <source>Left alt</source>
- <translation>左Alt键</translation>
- </message>
- <message>
- <source>Right meta</source>
- <translation>右meta键</translation>
- </message>
- <message>
- <source>Left meta</source>
- <translation>左meta键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="93"/>
+ <source>Fruits</source>
+ <translation>水果</translation>
</message>
<message>
- <source>A button</source>
- <translation>A 键</translation>
- </message>
- <message>
- <source>B button</source>
- <translation>B 键</translation>
- </message>
- <message>
- <source>X button</source>
- <translation>X 键</translation>
- </message>
- <message>
- <source>Y button</source>
- <translation>Y 键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="94"/>
+ <source>Banana</source>
+ <translation>香蕉</translation>
</message>
<message>
- <source>LB button</source>
- <translation>LB 键</translation>
- </message>
- <message>
- <source>RB button</source>
- <translation>RB 键</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="95"/>
+ <source>Apple</source>
+ <translation>苹果</translation>
</message>
<message>
- <source>Back button</source>
- <translation>返回键</translation>
- </message>
- <message>
- <source>Start button</source>
- <translation>开始键</translation>
- </message>
- <message>
- <source>Left stick</source>
- <translation>左摇杆</translation>
- </message>
- <message>
- <source>Right stick</source>
- <translation>右摇杆</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="96"/>
+ <source>Orange</source>
+ <translation>橙子</translation>
</message>
<message>
- <source>Left stick (Right)</source>
- <translation>右(左摇杆)</translation>
- </message>
- <message>
- <source>Left stick (Left)</source>
- <translation>左(左摇杆)</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="97"/>
+ <source>Lemon</source>
+ <translation>柠檬</translation>
</message>
<message>
- <source>Left stick (Down)</source>
- <translation>下(左摇杆)</translation>
- </message>
- <message>
- <source>Left stick (Up)</source>
- <translation>上(左摇杆)</translation>
- </message>
- <message>
- <source>Left trigger</source>
- <translation>左制动</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="98"/>
+ <source>Pineapple</source>
+ <translation>菠萝</translation>
</message>
<message>
- <source>Right trigger</source>
- <translation>右制动</translation>
- </message>
- <message>
- <source>Right stick (Down)</source>
- <translation>下(右摇杆)</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="99"/>
+ <source>Mango</source>
+ <translation>芒果</translation>
</message>
<message>
- <source>Right stick (Up)</source>
- <translation>上(右摇杆)</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="100"/>
+ <source>Peach</source>
+ <translation>桃子</translation>
</message>
<message>
- <source>Right stick (Right)</source>
- <translation>右(右摇杆)</translation>
- </message>
- <message>
- <source>Right stick (Left)</source>
- <translation>左(右摇杆)</translation>
- </message>
- <message>
- <source>DPad</source>
- <translation>DPad板</translation>
+ <location filename="../../../../QTfrontend/predefteams.h" line="101"/>
+ <source>Plum</source>
+ <translation>梅子</translation>
</message>
</context>
</TS>
--- a/share/hedgewars/Data/Locale/zh_CN.txt Thu Aug 30 12:47:41 2012 -0400
+++ b/share/hedgewars/Data/Locale/zh_CN.txt Thu Aug 30 13:02:19 2012 -0400
@@ -1,825 +1,47 @@
; Simplified Chinese locale
00:00=手榴弹
-00:01=集束炸弹
-00:02=火箭筒
-00:03=归巢的蜜蜂
-00:04=霰弹枪
-00:05=大锤
-00:06=跳过回合
+00:01=子母炸弹
+00:02=火箭炮
+00:03=UFO
+00:04=散弹枪
+00:05=气锤
+00:06=掠过
00:07=绳索
00:08=地雷
00:09=沙漠之鹰
00:10=炸药
00:11=球棒
-00:12=Shoryuken
+00:12=升龙拳
00:13=秒
00:14=降落伞
00:15=空袭
-00:16=地雷空袭
+00:16=地雷袭击
00:17=喷灯
-00:18=钢梁
+00:18=钢板
00:19=传送
-00:20=切换刺猬
+00:20=切换
00:21=迫击炮
00:22=鞭子
00:23=神风特工队
00:24=蛋糕
-00:25=引诱
+00:25=吸引
00:26=西瓜炸弹
00:27=地狱礼花
-00:28=钻头火箭
+00:28=钻地火箭
00:29=弹珠炮
-00:30=汽油弹空袭
-00:31=遥控轰炸机
+00:30=燃烧弹
+00:31=轰炸机
00:32=低重力
-00:33=增强伤害
-00:34=无敌
+00:33=附加伤害
+00:34=刀枪不入
00:35=加时
00:36=激光瞄准
-00:37=吸血
-00:38=狙击枪
-00:39=UFO
-00:40=燃烧瓶
-00:41=鸟儿
-00:42=传送器
-00:43=飞来的钢琴
-00:44=毒奶酪
-00:45=正弦能量炮
-00:46=火焰喷射器
-00:47=固定地雷
-00:48=大锤
-00:49=复苏
-00:50=电钻空袭
-00:51=土块
-01:00=开战!
-01:01=平局
-01:02= %1 胜利!
+01:00=战斗啦!
+01:01=平手
+01:02= %1 胜!
01:03=音量 %1%
01:04=暂停
-01:05=确定要退出? (是Y/否Esc)
-01:06=死亡模式!
-01:07=%1 剩余
-01:08=燃料
-01:09=同步中...
-01:10=使用本工具不会结束回合!
-01:11=您还不能用它!
-01:12=死亡模式前最后一回合!
-01:13=%1 回合倒计时!
-01:14=预备上, %1!
-
-; Event messages
-; Hog (%1) died
-; 02:00=%1 has kicked the bucket!
-02:00=%1 翘辫子了!
-; 02:00=%1 has seen the light!
-02:00=%1 目睹圣光降临!
-; 02:00=%1 never saw that coming!
-02:00=%1 无法瞑目!
-; 02:00=%1 waves goodbye!
-02:00=%1 向大家挥手道别。
-; 02:00=%1 has gone to a better place!
-02:00=%1 去了极乐世界!
-; 02:00=%1 meets his maker!
-02:00=%1 去见造物主了!
-; 02:00=%1 can hang on no longer!
-02:00=%1 再也受不了了!
-; 02:00=%1 has done his duty!
-02:00=%1 完成了他的使命!
-; 02:00=%1 makes the ultimate sacrifice!
-02:00=%1 做了最大的牺牲!
-; 02:00=%1 departs this mortal coil!
-02:00=%1 摆脱了躯壳的束缚!
-; 02:00=%1 makes like a tree and leaves!
-02:00=%1 叶落归根。
-; 02:00=%1 has timed out!
-02:00=%1 大限已至。
-; 02:00=%1 says peace out!
-02:00=%1 悄然离场了。
-; 02:00=%1 will be fondly remembered!
-02:00=%1 永远活在我们心中!
-; 02:00=%1 has an aneurysm!
-02:00=%1 不治而亡。
-; 02:00=%1 leaves behind a wife and child
-02:00=%1 留下一家孤儿寡母
-; 02:00=%1 has launched his last bazooka
-02:00=%1 发射了最后一发火箭弹
-; 02:00=%1 has tossed his last grenade
-02:00=%1 扔出了最后一枚手榴弹
-; 02:00=%1 has baked his last cake
-02:00=%1 烘烤了最后一块蛋糕
-; 02:00=%1 has swung on his last rope
-02:00=%1 最后一次甩出了绳索
-; 02:00=%1 has called his last airstrike
-02:00=%1 最后一次呼叫空袭
-; 02:00=%1 has pumped his last shotgun
-02:00=%1 最后一次抽出了霰弹枪
-; 02:00=%1 has thrown his last melon
-02:00=%1 最后一次扔出了西瓜炸弹
-; 02:00=%1 has drawn his last deagle
-02:00=%1 最后一次拔出了沙漠之鹰
-; 02:00=%1 took one shot too many
-02:00=%1 挨了太多枪子了
-; 02:00=%1 could really have used a health crate
-02:00=%1 真该用下医疗包的
-; 02:00=%1 has gone to play a better game
-02:00=%1 去玩更有意思的游戏去了
-; 02:00=%1 has ragequit life
-02:00=%1 拔网线了!
-; 02:00=%1 fails
-02:00=%1 失败了
-; 02:00=Poor poor %1...
-02:00=可怜的 %1...
-; 02:00=%1 prefers wormux
-02:00=%1 更喜欢 Warmux
-; 02:00=%1 has been blocking shots with his face
-02:00=%1 勇于面对子弹,结果相当惨烈
-; 02:00=%1 is a hero amongst me...err..hogs
-02:00=%1 是位英雄……额……在刺猬当中
-; 02:00=%1 finds his place in Valhalla
-02:00=%1 在勇者纪念碑上找到了自己的位置
-; 02:00=%1 has left the building
-02:00=%1 离开了这间屋子
-; 02:00=%1 goes the way of the dinosaurs
-02:00=%1 步上了恐龙的道路
-; 02:00=%1 brings hedgehogs one step closer to extinction
-02:00=%1 让刺猬向物种灭绝更近了一步
-; 02:00=%1 brings a tear to my eye
-02:00=%1 带走了我一滴眼泪
-; 02:00=%1 is an ex-hog
-02:00=%1 生前是一只刺猬
-; 02:00=%1 is pushing up the daisies
-02:00=%1 被菊花簇拥
-; 02:00=%1 has ceased to be
-02:00=%1 被删除了
-; 02:00=Say goodbye to %1
-02:00=对 %1 说再见
-; 02:00=No hope left for %1
-02:00=%1 没有希望了
-; 02:00=%1 faces the final curtain
-02:00=%1 面容被落下的帷幕遮住了
-; 02:00=Smoke 'em if you got 'em, %1
-02:00=%1 抓紧时间实现你最后的愿望吧
-; 02:00=%1 suffers a Spontaneous Massive Existence Failure
-02:00=%1 遭遇了自发性大规模故障
-; 02:00=%1 has passed on
-02:00=%1 走了
-; 02:00=%1 is stone dead
-02:00=%1 永垂不朽
-; 02:00=%1 is no more
-02:00=%1 不在了
-; 02:00=%1 has expired
-02:00=%1 已故
-; 02:00=Bereft of life, %1 rests in peace
-02:00=%1 安详地躺着
-; 02:00=%1 joins the choir invisible
-02:00=%1 加入了隐形唱诗班
-; 02:00=Farewell %1, we hardly knew ye!
-02:00=%1, 永别了,我们还没熟悉你呢!
-; 02:00=%1 had a low tolerance for being shot
-02:00=%1 抗打击能力不足
-; 02:00=%1 could have used an extra life
-02:00=%1 本该有第二条命的
-; 02:00=Is there a doctor in the house?
-02:00=这里有医生吗?
-
-; Hog (%1) drowned
-; 02:01=%1 plays submarine!
-02:01=%1 去玩潜水艇了!
-; 02:01=%1 mimics the Titanic!
-02:01=%1 学泰坦尼克去了!
-; 02:01=%1 swims like a stone!
-02:01=%1 石沉大海!
-;02:01=%1 checks out the deep end
-02:01=%1 说要去检查深水区
-;02:01=%1 goes glug glug glug
-02:01=%1 :“咕噜咕噜咕噜……”
-;02:01=%1 goes splash
-02:01=%1 栽入水花里
-;02:01=%1 forgot his armbands
-02:01=%1 忘记了戴臂章
-;02:01=%1 really should have taken swimming lessons
-02:01=%1 真的该去学游泳的
-;02:01=%1 left his surfboard at home
-02:01=%1 把救生圈忘家了
-;02:01=%1 is washed up
-02:01=%1 被冲走了
-;02:01=%1 is one soggy hog
-02:01=%1 脑子进水了
-;02:01=%1 forgot to bring his life jacket
-02:01=%1 忘记带救生衣了
-;02:01=%1 goes splish splash splish
-02:01=%1 实现了水上飘,身后一片水花荡漾
-;02:01=%1 is sleeping with the fishes
-02:01=%1 将会和鱼睡在一起
-;02:01=%1 thinks the water physics suck in this game
-02:01=%1 认为这游戏关于落水的设定糟糕透了
-;02:01=%1 looks thirsty
-02:01=%1 看样子很渴
-;02:01=the sea claims %1
-02:01=大海吞没了 %1
-;02:01=%1 is lost at sea
-02:01=%1 在海上迷失了
-;02:01=%1 should have brought his scuba gear
-02:01=%1 应该要带潜水工具的
-;02:01=%1 gets a burial at sea
-02:01=%1 享受到了海葬待遇
-;02:01=%1 has that sinking feeling
-02:01=%1 觉得自己在下沉
-;02:01=%1 is practicing his backstroke
-02:01=%1 终于能实践自己的游泳理论了
-;02:01=%1 goes in search of the Titanic
-02:01=%1 去泰坦尼克号寻宝了
-;02:01=%1 is not Jesus
-02:01=很遗憾 %1 不是耶稣
-;02:01=%1 is finding Nemo
-02:01=%1 正在寻找Nemo
-;02:01=%1 springs a leak
-02:01=%1 钻入了一个水洼
-;02:01=You've gotta wonder how many hogs are down there
-02:01=你会知道海底还会有多少同伴的
-;02:01=%1 makes the ocean slightly higher
-02:01=%1 让海平面高了那么一点, 就一点
-;02:01=%1 didn't enlist in the Navy
-02:01=很明显 %1 没在海军服役过
-;02:01=%1 is doing his impersonation of a dead fish
-02:01=%1 其实是在模仿死鱼啦
-;02:01=At least you didn't go down the toilet, %1
-02:01=还好 %1 你不是掉进了厕所
-;02:01=Sonic couldn't swim and neither can %1
-02:01=索尼克不能游泳, %1 也一样
-;02:01=%1 wants to play Ecco the dolphin
-02:01=%1 想玩海底漫步
-;02:01=%1 has gone to visit Aquaria
-02:01=%1 去水族馆报到了
-;02:01=%1 has found the lost city of Atlantis
-02:01=%1 找到了传说中的亚特兰蒂斯城
-;02:01=%1 aims for the lead role in Bioshock 3
-02:01=%1 立志在生化奇兵3中担任头号角色
-;02:01=Your doggy paddle could use a little work, %1
-02:01=你的狗刨式会有点用的, %1
-;02:01=%1 should have brought a jet ski
-02:01=%1 竟然没带摩托艇
-;02:01=%1 doesn't like watersports
-02:01=%1 不喜欢水上运动
-;02:01=%1 is forever blowing bubbles
-02:01=%1 学会了绝技: 神风吹泡泡
-;02:01=%1 is short of a raft
-02:01=%1 需要一个救生艇
-;02:01=%1 thinks salt water is good for the skin
-02:01=%1 认为盐水对皮肤有好处
-;02:01=%1 gets salt water in his wounds
-02:01=%1 的伤口沾上了盐水
-;02:01=%1 has walked the plank
-02:01=%1 错过了那块木板
-;02:01=%1 has a bath
-02:01=%1 洗澡去了
-;02:01=%1 is wet wet wet
-02:01=%1 全身都是水,水,水……
-;02:01=%1 gets his quills wet
-02:01=%1 把刺全弄湿了
-;02:01=It's Davy Jones' locker for %1
-02:01=深海阎王正在等待 %1
-
-; Round starts
-; 02:02=Let's fight!
-02:02=开战吧!
-; 02:02=Armed and ready!
-02:02=武装准备!
-;02:02=Let's get ready to rumble!
-02:02=准备对轰!
-;02:02=Let's get it on!
-02:02=为胜利而战吧!
-;02:02=Let's get this party started
-02:02=让我们开始这个派对吧
-;02:02=Last hog standing wins
-02:02=胜利属于最后一个生还者
-;02:02=Let's go!
-02:02=出发吧!
-;02:02=Let's rock!
-02:02=一起摇滚吧!
-;02:02=Let's jam!
-;02:02=It's beginning...
-02:02=开始了...
-;02:02=This is the start of something big
-02:02=这是一个伟大的开始
-;02:02=Welcome to Hedgewars
-02:02=欢迎来到刺猬大作战
-;02:02=Welcome to the front lines
-02:02=欢迎来到前线
-;02:02=Crush your enemies!
-02:02=目标:粉碎你的敌人!
-;02:02=May the best hog win
-02:02=祝愿胜利属于最厉害的刺猬!
-;02:02=Victory or death
-02:02=胜利或死亡
-;02:02=To the victor goes the spoils
-02:02=战利品只属于胜利者
-;02:02=Losing is not an option
-02:02=字典里面应该没有"输"这个字的
-;02:02=Cry havoc! Let loose the hogs of war!
-02:02=放声喊吧! 这是刺猬的战争!
-;02:02=Hedgewars, brought to you by Hedgewars.org
-02:02=欢迎来到刺猬大作战, Hedgewars.org 为你呈现
-02:02=GL HF
-;02:02=Just count yourself lucky you're not up against Tiyuri
-02:02=你看你多幸运不是在对战 Tiyuri
-;02:02=Just count yourself lucky you're not up against unC0Rr
-02:02=你看你多幸运不是在对战 unC0Rr
-;02:02=Just count yourself lucky you're not up against Nemo
-02:02=你看你多幸运不是在对战 Nemo
-;02:02=Just count yourself lucky you're not up against Smaxx
-02:02=你看你多幸运不是在对战 Smaxx
-;02:02=Just count yourself lucky you're not up against Jessor
-02:02=你看你多幸运不是在对战 Jessor
-;02:02=Give it your all!
-02:02=展现你的一切吧!
-;02:02=The losers do the cleaning up!
-02:02=输的要罚扫厕所!
-;02:02=Let the fight of the millenium begin
-02:02=宇宙之战开始了
-;02:02=Let the fight of the century begin
-02:02=世纪之战开始了
-;02:02=Let the fight of the decade begin
-02:02=正义之战开始了
-;02:02=Let the fight of the year begin
-02:02=年度争霸战开始了
-;02:02=Let the fight of the month begin
-02:02=本月之星争霸战开始了
-;02:02=Let the fight of the week begin
-02:02=每周擂主争霸战开始了
-;02:02=Let the fight of the day begin
-02:02=本日最强入围赛开始了
-;02:02=Let the fight of the hour begin
-02:02=我们能战一小时!
-;02:02=Do your best!
-02:02=诸君努力!
-;02:02=Destroy the enemy!
-02:02=目标: 摧毁敌人
-;02:02=Good luck
-02:02=祝你好运
-;02:02=Have fun~
-02:02=开心玩~
-;02:02=Fight the good fight
-02:02=漂亮的战斗
-;02:02=Fight dirty
-02:02=不择手段
-;02:02=Fight with honour
-02:02=满载荣誉而战
-;02:02=Don't give up
-02:02=教练告诉你: 别放弃
-;02:02=Never surrender
-02:02=永不屈服!
-;02:02=Rock 'em and sock 'em!
-02:02=蹂虐对手
-;02:02=Let the fragfest begin!
-02:02=积分赛开始!
-;02:02=I hope you're ready for a tussle!
-02:02=你准备好恶战了么?
-;02:02=Go Go Go!
-02:02=冲冲冲!
-;02:02=Hedgehogs advance!
-02:02=刺猬向前冲!
-;02:02=Bring it to them!
-02:02=炸飞他们!
-;02:02=Have no fear!
-02:02=无所畏惧!
-;02:02=Be brave and conquer
-02:02=敢于征服!
-
-; Round ends (win; unused atm)
-02:03=回合结束(胜利)
-
-; Round ends (draw; unused atm)
-02:04=回合结束(平局)
-
-; New health crate
-;02:05=Incoming aid!
-02:05=医疗包!
-;02:05=Medic!
-02:05=急救包!
-;02:05=First aid from the skies!
-02:05=救援物资空运来了!
-;02:05=A health pack for you
-02:05=你的医疗包到了
-;02:05=Good health.. in box form!
-02:05=生命就在那箱子里!
-;02:05=The doctor calls
-02:05=医生的紧急呼叫
-;02:05=Fresh band-aids!
-02:05=新鲜创可贴!
-;02:05=This will make you feel better
-02:05=吃了这个感觉会好些的...
-;02:05=A Hi-Potion! Whoops wrong game
-02:05=兴奋剂!呃。。。走错地方了
-;02:05=A pick-me-up!
-02:05=万金油!
-;02:05=Grab it
-02:05=捉住它
-;02:05=A healthy snack
-02:05=健康食品
-;02:05=A remedy to pain
-02:05=止痛饼来了
-;02:05=Correct Dosage: as many as you can find!
-02:05=使用方法: 吃得越多越好
-;02:05=Urgent delivery
-02:05=紧急物资
-;02:05=Supplies!
-02:05=补给!
-
-; New ammo crate
-; 02:06=More weapons!
-02:06=武器!
-;02:06=Reinforcements!
-02:06=增援!
-;02:06=Lock and load!
-02:06=准备!
-;02:06=I wonder what weapon is in there?
-02:06=我要的那个会在的吧...
-;02:06=Supplies!
-02:06=补给!
-;02:06=What could be inside?
-02:06=里面会有啥呢?
-;02:06=Christmas comes early in Hedgewars
-02:06=刺猬大作战每天都是圣诞节
-;02:06=A present!
-02:06=礼物送到!
-;02:06=Special delivery!
-02:06=特快专递!
-;02:06=It was a nightmare getting this through customs
-02:06=本局的噩梦来了
-;02:06=Destructive toys from the heavens
-02:06=玩具从天堂掉下来了
-;02:06=Warning! Contents Volatile
-02:06=警告! 内含危险物品
-;02:06=Pick it up or blow it up, choice is yours
-02:06=拿走或打爆, 随你
-;02:06=Goodies!
-02:06=好玩意儿!
-;02:06=Mmmmm Ammo
-02:06=弹药!!!!
-;02:06=A box of destructive power
-02:06=潘朵拉之盒
-;02:06=Airmail!
-02:06=天降之物!
-;02:06=Whatever's in that box, it ain't pizza
-02:06=无论里面是啥, 那肯定不会是软妹子
-;02:06=Get it!
-02:06=拿走它!
-;02:06=Weapon drop incoming
-02:06=武器掉下来了!
-;02:06=Don't let the enemy grab that!
-02:06=别让敌人拿了!
-;02:06=Shiny new toys!
-02:06=新玩具!
-;02:06=A mysterious box!
-02:06=神秘的箱子!
-
-; New utility crate
-; 02:07=Tooltime!
-02:07=工具箱!
-;02:07=This could come in handy...
-02:07=这可能派上用场
-;02:07=Utilities!
-02:07=工具!
-;02:07=Utilise this box
-02:07=工具在这里!
-;02:07=Watch out below
-02:07=快看这里!
-;02:07=More utilities!
-02:07=更多选择更多欢笑, 尽在工具包
-;02:07=Tools for you!
-02:07=一堆工具, 送给你!
-;02:07=This should be good!
-02:07=这看见起来蛮好...
-;02:07=Use this wisely
-02:07=使用这个才是明智的选择
-;02:07=Ooo this box is heavy
-02:07=好重...好重...
-;02:07=You might need this
-02:07=会有用的
-
-; Hog (%1) skips his turn
-; 02:08=%1 is sooo boring...
-02:08=%1 太无聊了...
-;02:08=%1 couldn't be bothered
-02:08=%1 不想被打扰!
-;02:08=%1 is one lazy hog
-02:08=%1 太懒了
-;02:08=%1 is thoughtless
-02:08=%1 太轻率了
-;02:08=%1 gave up
-02:08=%1 放弃了
-;02:08=You snooze you lose, %1
-02:08=不认真你就输了, %1
-;02:08=%1 shamelessly skips
-02:08=%1 无耻的跳过了本回合
-;02:08=%1 is really lazy
-02:08=%1 真的太懒了
-;02:08=%1 needs a little more motivation
-02:08=%1 没有动力了
-;02:08=%1 is a pacifist
-02:08=%1 是和平主义者
-;02:08=%1 has a breather
-02:08=%1 需要喘息一下
-;02:08=%1 has a rest
-02:08=%1 需要休息
-;02:08=%1 chills out
-02:08=%1 发冷了
-;02:08=%1 has no faith in his own abilities
-02:08=%1 做啥都没信心了
-;02:08=%1 decides to do nothing at all
-02:08=%1 决定啥都不做
-;02:08=%1 lets the enemy destroy itself
-02:08=%1 认为敌人会自杀的
-;02:08=%1 would be terrible at parties
-02:08=%1 将会陷入可怕的事件中
-;02:08=%1 hides out
-02:08=%1 说:“你看不到我,你看不到我……”
-;02:08=%1 has decided to pass on this opportunity
-02:08=%1 已经决定放弃这个机会
-;02:08=%1 decides the best thing he can do is...nothing
-02:08=%1 决定他现在最应该做的是......坐着不动
-;02:08=%1 is a big wuss
-02:08=%1 大笨蛋!
-;02:08=Buck Buck Buck, %1 is a chicken
-02:08=%1 是小鸡鸡
-;02:08=%1 is looking a little yellow
-02:08=%1 看来有点印堂发黑
-;02:08=%1 is a coward!
-02:08=%1 是懦夫!
-;02:08=%1 is waiting for sudden death
-02:08=%1 在等待突然死亡模式
-;02:08=%1 is not the fighting type
-02:08=%1 不是战斗系的
-;02:08=%1 is reconsidering his purpose in life
-02:08=%1 正在重新寻找他的人生
-;02:08=%1 was never much of a good shot anyway
-02:08=%1 从来没一次打准的
-;02:08=%1 didn't want to join the army in the first place
-02:08=%1 不想参军
-;02:08=Stop wasting our time, %1
-02:08=别浪费时间了! %1
-;02:08=I'm dissapointed in you, %1
-02:08=我对你失望了, %1
-;02:08=Come on, you can do better than that %1
-02:08=%1 明明就能做的更好的
-;02:08=%1's will has broken
-02:08=%1 会被打飞的
-;02:08=%1 apparently has better things to do
-02:08=%1 显然有更好的事情等着做
-;02:08=%1 is scared stiff
-02:08=%1 怕刺激
-;02:08=%1 has fallen asleep
-02:08=%1 睡着了
-
-; Hog (%1) hurts himself only
-; 02:09=%1 should practice aiming!
-02:09=%1 该练练瞄准了!
-; 02:09=%1 seems to hate himself.
-02:09=%1 似乎看自己很不爽。
-; 02:09=%1 is standing on the wrong side!
-02:09=%1 在表演乌龙!
-; 02:09=%1 makes like an emo
-02:09=%1 以为自己无敌
-; 02:09=%1 was holding his weapon the wrong way around
-02:09=%1 好像把武器拿错方向了
-;02:09=%1 is a little sadistic
-02:09=%1 有点施虐狂
-;02:09=%1 is a masochist
-02:09=%1 是受虐狂
-;02:09=%1 has no instinct of self-preservation
-02:09=%1 根本不会自我保护
-;02:09=%1 messed up
-02:09=%1 乱套了
-;02:09=%1 screwed up
-02:09=%1 搞砸了
-;02:09=That was a poor shot, %1
-02:09=%1 这一发真渣
-;02:09=%1 is a little too careless with dangerous weapons
-02:09=%1 太不小心用那些危险的玩意了
-;02:09=%1 should consider a change of career
-02:09=%1 正在考虑转职
-;02:09=Worst. Shot. Ever!
-02:09=更差! 最差! 非常差!
-;02:09=No no no %1, you shoot at the ENEMY!
-02:09=No no no %1, 你要打敌人!
-;02:09=%1 should only be destroying the enemy
-02:09=%1 应该消灭敌人才对
-;02:09=%1 moves one step closer to suicide
-02:09=%1 正在走向自杀
-;02:09=%1 aids the enemy
-02:09=%1 帮助敌人
-;02:09=That was stupid %1
-02:09= %1 是笨蛋
-;02:09=%1 lives by the mantra of "no pain, no gain"
-02:09=%1 贯彻“不付出,何收获“的原则
-;02:09=%1 is confused
-02:09=%1 思维混乱了
-;02:09=%1 hurt itself in its confusion
-02:09=%1 在混乱中攻击自己
-;02:09=%1 has a knack for embarrassing himself
-02:09=%1 正在为自己尴尬
-;02:09=%1 is a klutz!
-02:09=%1 就是一个笨蛋!
-;02:09=%1 is clumsy
-02:09=%1 笨手笨脚的
-;02:09=%1 shows the enemy what he's capable of
-02:09=%1 展示了自己的能力
-;02:09=%1 can't be expected to be perfect all the time
-02:09=%1 不能每次都完美
-;02:09=Don't worry %1, pobody's nerfect
-02:09=不用担心 %1 , 人都不是完美的
-;02:09=%1 totally did that on purpose
-02:09=%1 这么做真的是有目的
-;02:09=I won't tell anyone if you don't, %1
-02:09=我不会把 %1 的事情到处说的
-;02:09=How embarrassing!
-02:09=何等的失态!
-;02:09=I'm sure nobody saw that %1
-02:09=保证,决没人看到 %1 做什么
-;02:09=%1 needs to review his field manual
-02:09=%1 需要复习说明书
-;02:09=%1's weapon clearly malfunctioned
-02:09=%1 的武器很明显坏了
-
-; Hog shot an home run (using the bat and another hog)
-; 02:10=Home Run!
-02:10=全垒打!
-; 02:10=A bird, a plane, ...
-02:10=一只鸟,一架飞机,...
-; 02:10=That one is out!
-02:10=那一位出界了!
-
-; Hog (%1) has to leave (team is gone)
-02:11=%1 必须上床了
-02:11=%1 玩的过火了,休息一下
-02:11=发射!这位已经被送出去
-02:11=%1 必须走了
-
-; Weapon Categories
-03:00=定时手雷
-03:01=定时手雷
-03:02=弹道武器
-03:03=制导武器
-03:04=枪 (多发子弹)
-03:05=钻孔工具
-03:06=动作
-03:07=移动工具
-03:08=接近式炸弹
-03:09=枪 (多发子弹)
-03:10=BOOM!
-03:11=咚!
-03:12=武术
-03:13=未使用
-03:14=移动工具
-03:15=空投打击
-03:16=空投打击
-03:17=打洞工具
-03:18=工具
-03:19=移动工具
-03:20=动作
-03:21=弹道武器
-03:22=叫我主人!
-03:23=武术 (真的!)
-03:24=蛋糕不是谎言!
-03:25=化妆的诱惑
-03:26=果汁手雷
-03:27=烫手手雷
-03:28=弹道武器
-03:29=弹道武器
-03:30=空投打击
-03:31=遥控飞机(不是玩具!)
-03:32=临时效果
-03:33=临时效果
-03:34=临时效果
-03:35=临时效果
-03:36=临时效果
-03:37=临时效果
-03:38=枪 (多发子弹)
-03:39=移动工具
-03:40=燃烧弹
-;03:41=Huge fan of Squawks
-03:41=粉丝的呼喊
-;03:42=I'm making a note here...
-03:42=我将在此做一记录...
-; the misspelled "Beethoven" is intentional (-> to beat)
-;03:43=Performing Beathoven's deadly sonata
-03:43=正在演奏贝揍芬的死亡奏鸣曲
-;03:44=Best before: 1923
-03:44=此日期前最佳:1923
-;03:45=The power of science
-03:45=科学的力量
-;03:46=Hot Hot Hot!
-03:46=烫烫烫!
-;03:47=Stick these somewhere useful!
-03:47= 呆在有利的地方!
-;03:48=It's Hammer time!
-03:48=大锤威武!
-;03:49=Does what you guess
-03:49=尽情猜想
-;03:50=Moles fan
-03:50=地道战
-
-; Weapon Descriptions (use | as line breaks)
-04:00=使用简单的手榴弹攻击敌人.|定时器倒数到0就会爆炸.|1-5: 设定定时器|攻击键: 按住蓄力.
-04:01=使用集束手雷攻击敌人.|定时器倒数到0就会爆炸并裂开成几块.|1-5: 设定定时器|攻击键: 按住蓄力.
-04:02=使用弹道导弹攻击敌人.|受风力影响.|攻击键: 按住蓄力.
-04:03=发射一个制导导弹攻击所选目标.|如果要精确打击就不要使用全力发射.|光标: 选定目标|攻击键: 按住蓄力.
-04:04=霰弹枪有两排子弹.|因为是霰弹枪所以不一定要对准敌人.|攻击键: 开枪 (两发)
-04:05=向地底出发! 使用他就能在地面|打个洞, 就能去其他地方.|攻击键: 开始/停止打洞
-04:06=闷了? 没法打? 保存体力? 没问题!|跳过这回合就可以了, 懦夫!|攻击键: 跳过回合
-04:07=用绳索就可以去很远的地方.|还能空降到别的刺猬身上丢手榴弹呢.|攻击键: 发射/收回绳索|长跳键: 发射手榴弹或其他武器
-04:08=你能用地雷阻止敌人靠近.|还能静悄悄的放在敌人脚下.|一定要在爆炸前逃离到安全的地方!|攻击键: 把地雷放在你的脚下
-04:09=自我感觉准头不行? |沙漠之鹰有4颗子弹呢.|攻击键: 开枪 (四发)
-04:10=使用强力炸药就是一个明智的选择.|这是最经典的轰炸方式.|攻击键: 把炸药放在你的脚下
-04:11=把敌人打飞, 飞出地图或者飞进水里.|或者把地雷打过去?|攻击键: 敲打你面前的所有东西
-04:12=这就是武术的威力!|致命的气功!|攻击键: 使用升龙拳
-04:13=UNUSED
-04:14=有恐高症? 拿降落伞吧.|他能慢慢的安全的把你带到地面.|攻击键: 展开降落伞
-04:15=呼叫一架飞机轰炸你的敌人.|左/右方向键: 决定攻击方向|光标: 选定目标
-04:16=呼叫一架飞机投下大量地雷.|左/右方向键: 决定攻击方向|光标: 选定目标
-04:17=需要个安全的地方? 使用喷灯为你挖掘一条安全的隧道!|攻击键: 开始/停止挖掘
-04:18=喷灯还不够?还要个更安全的地方?|建造若干条大梁挡住吧.|左/右方向键: 选择梁的方向|光标: 建造
-04:19=适当的时候撤退是比所有的攻击|更安全的选择|光标: 选择传送目标
-04:20=可以让你更换当前使用的刺猬.|攻击键: 启动切换功能
-04:21=用炮弹发射器发射一个手榴弹样|的东西. 在爆炸之后会裂开成小块|攻击键: 全力发射
-04:22=这不只是女王才用的东西!|这鞭子能解决很多问题, 比如说那些|喜欢站在悬崖边上的小屁孩.|攻击键: 鞭打你面前的一切东西
-04:23=自杀式炸弹袭击向来好用!|用你的一条命攻击直线上的一切东西并爆炸.|攻击键: 启动自杀性攻击
-04:24=生日快乐! 嗱, 放下这个蛋糕, 他|就会走到敌人身边然后爆炸.| 而且能贴着地形走.|攻击键: 让蛋糕开始/结束走路
-04:25=使用美人计让敌人向着你这个方向跳|(比如跳进海里).|攻击键: 使用本工具诱惑敌人
-04:26=把这个多汁的西瓜扔向敌人!| 一旦定时器倒数完, 就会|炸成几块更强力的炸弹.|攻击键: 按住蓄力.
-04:27=让地狱的礼花在敌人头上绽放!|这真的是危险品, 使用时候记得原理|爆炸之后还会燃烧好一阵子|攻击键: 按住蓄力.
-04:28=本火箭在发射后将会钻到地里|一旦燃料用完或者打穿地面就会爆炸.|攻击键: 按住蓄力.
-04:29=还记得小时候玩的玻璃球么?|不过这个是炸弹版. 发射大量的小玻|璃球然后爆炸|攻击键: 全力发射|上/下方向键: 发射过程中更换方向
-04:30=呼叫一架飞机空投燃烧弹.|用得好的话会造成巨大伤害.|左/右方向键: 决定攻击方向|光标: 选定目标
-04:31=啊哈, 遥控飞机除了能帮你|收集物品之外. 还能空投炸弹.|攻击键: 飞机起飞/投放炸弹|长跳键: 战场之神|上/下方向键: 控制方向
-04:32=低重力装置能影响更多东西!| 除了跳得更远之外还能让|敌人飞得更远.|攻击键: 激活
-04:33=有时候致命打击还是不够过瘾.|攻击键: 激活
-04:34=你打不到我!|攻击键: 激活
-04:35=时间流逝得很快, 你也知道|刺猬腿短.|攻击键: 激活
-04:36=好吧, 你最后还是承认自己眼神不好.|高科技还是能帮你不少的.|攻击键: 激活
-04:37=不用害怕白天.|这只能本回合有效, 可以把造成的伤害变|成自己的血量 .|攻击键: 激活
-04:38=你也知道狙击枪的威力,|能打比较远的地方.|攻击键: 射击 (2发子弹)
-04:39=驾驶飞碟可以飞到地图上的任何角落.|不过这个东西连发明者都认为很难用.|攻击键: 激活|上/左/右方向键: 向某方向飞|前跳:攻击敌人
-04:40=把地面填满汽油然后....|攻击键: 按住蓄力.
-;04:41=The evidence nature might even top the flying|saucer. Birdy can carry your hog around and|drop eggs on your enemies!|Be quick, as using Birdy eats into your turn|time!|Attack: Activate and drop eggs|Up/Left/Right: Flap in one direction
-04:41=自然的力量胜过飞盘的证据。|鸟儿可以携带刺猬并在敌人头上下蛋!|要快!使用鸟儿会消耗回合时间!|攻击键: 激活鸟儿和下蛋|上/左/右方向键: 向某方向飞
-;04:42=This portable portal device is capable|of instantly transporting you, your enemies,|or your weaponry between two points on the|terrain.|Use it wisely and your campaign will be a...|HUGE SUCCESS!|Attack: Shoot a portal|Switch: Cycle portal colours
-04:42=移动传送装置|迅速传输自己或者敌人或者|你的武器,直接连接|地表的两个不同位置。|如果用的聪明那你的战斗将是一场……|巨大的胜利!|攻击键: 发射一个传送点|切换键: 改变颜色
-;04:43=Make your musical debut an explosive success!|Drop a piano from the heavens, but beware...|someone needs to play it, and that may cost you|your life!|Cursor: Select target region|F1-F9: Play the piano
-04:43=音乐细胞的迸发!|钢琴从天堂降落,带|着演奏者最终回归天堂|光标: 选择目标区域|F1-F9:演奏钢琴
-04:44=这不是奶酪!而是生化武器!|爆炸只有一次,带来的毒害是深远的!|1-5: 设定定时器|攻击键: 按住蓄力
-;04:45=All those physics classes have finally |paid off, launch a devastating Sine |wave at your foes. |Watch out, this weapon packs quite a kick. (This weapon is incomplete)|Attack: Shoot
-04:45=全部物理阶级最终|转化为正弦波动|留心,力是相对的|攻击键: 发射
-;04:46=Cover your foes with sizzling liquid flame.|Heartwarming!|Attack: Activate|Up/Down: Continue aiming|Left/Right: Modify spitting power
-04:46= 用满腔的火焰虐待你的对手吧。|攻击键: 激活|上/下方向键: 改变攻击方向|左/右方向键: 调整喷射距离
-;04:47=Double the fun with two spiky, sneaky, sticky mines.|Set up a chain reaction or defend yourself (or both!)|Attack: Hold to shoot with more power (twice)
-04:47=两次机会双重乐趣,隐蔽且黏着的地雷。|利用脑力造成连锁反应!|攻击键: 按住蓄力(两发)
-;04:48=Why should the moles get all the abuse?|Wacking a hog can be just as fun! A good|blow from this hammer will shave off one|third of a hog's health and plunge them|underground.|Attack: Activate
-04:48=痛扁刺猬:用力一锤|将使中者镶入地表,削减它健康的1/3.|攻击键: 打
-;04:49=Resurrect your friends!|But beware that this also resurrects your foes.|Attack: Keep attack pressed to resurrect slowly|Up: Accelerate resurrection
-04:49=复苏|注意,一视同仁|使用: 按住使用键|上: 提高速率
-
-; Game goal strings
-;05:01=The following rules apply
-05:01= 将应用以下规则
-;05:02=Forts: Defend your fortress; vanquish your enemies!
-05:02= 城堡: 守住你的城堡; 削平你的敌人!
-;05:03=Low Gravity: Watch your step
-05:03= 低重力: 注意脚步
-;05:04=Invulnerability: Hogs are (almost) invulnerable
-05:04=无敌: 刺猬不受伤害
-;05:05=Vampirism: Hogs will be healed for the damage dealt
-05:05=吸血: 敌人失去的就是我的
-;05:06=Karma: Hogs will be damaged for the damage dealt
-05:06=因果效应: 伤害有多少,自己都知道
-;05:07=Protect the King: Don't let your king die!|Place the King: Pick a protected starting point for your King
-05:07=保护国王: 国王不能死!|放置国王: 为国王选择安全的起始地点
-;05:08=Place Hedgehogs: Place your hogs before the game starts
-05:08=选择起始点: 游戏开始前手动放置刺猬
-;05:09=Artillery: Hogs can't walk to change position
-05:09=远程打击: 不许动!
-;05:10=Indestructible Terrain: Most weapons won't destroy terrain
-05:10=无损地表: 多数武器无法改变地形
-;05:11=Shared Ammo: All teams of the same color share their ammunition
-05:11=共享装备: 同色的刺猬共享它们的装备
-;05:12=Mine Timers: Mines will detonate after %1 second(s)
-05:12=地雷定时器: %1 秒起爆
-;05:13=Mine Timers: Mines will detonate instantly
-05:13=地雷定时器: 立即起爆
-;05:14=Mine Timers: Mines will detonate after 0 - 3 seconds
-05:14=地雷定时器: 0-3 秒起爆
-;05:15=Damage Modifier: All weapons will do %1% damage
-05:15=伤害修正: 武器伤害使用 %1% 修正值
-;05:16=Health of all hogs is reset on end of turn
-05:16=所有活着的刺猬回合结尾时彻底恢复健康
-;05:17=AI hogs respawn on death
-05:17=AI刺猬即时复活
-;05:18=Unlimited Attacks
-05:18=无限攻击法则
-;05:19=Weapons are reset on end of turn
-05:19=武器在回合结束时重置
-;05:20=Weapons are not shared between hogs
-05:20=刺猬的武器无法分享
+01:05=退出 (Y/Esc)?
+01:06=出现紧急情况!
--- a/tools/PascalParser.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/PascalParser.hs Thu Aug 30 13:02:19 2012 -0400
@@ -19,7 +19,7 @@
pascalUnit = do
comments
- u <- choice [program, unit, systemUnit]
+ u <- choice [program, unit, systemUnit, redoUnit]
comments
return u
@@ -270,12 +270,12 @@
char ';'
comments
forward <- liftM isJust $ optionMaybe (try (string "forward;") >> comments)
- many functionDecorator
+ inline <- liftM (any (== "inline;")) $ many functionDecorator
b <- if isImpl && (not forward) then
liftM Just functionBody
else
return Nothing
- return $ [OperatorDeclaration i rid ret vs b]
+ return $ [OperatorDeclaration i rid inline ret vs b]
funcDecl = do
@@ -295,21 +295,24 @@
char ';'
comments
forward <- liftM isJust $ optionMaybe (try (string "forward;") >> comments)
- many functionDecorator
+ inline <- liftM (any (== "inline;")) $ many functionDecorator
b <- if isImpl && (not forward) then
liftM Just functionBody
else
return Nothing
- return $ [FunctionDeclaration i ret vs b]
+ return $ [FunctionDeclaration i inline ret vs b]
- functionDecorator = choice [
- try $ string "inline;"
- , try $ caseInsensitiveString "cdecl;"
- , try $ string "overload;"
- , try $ string "export;"
- , try $ string "varargs;"
- , try (string "external") >> comments >> iD >> optional (string "name" >> comments >> stringLiteral pas)>> string ";"
- ] >> comments
+ functionDecorator = do
+ d <- choice [
+ try $ string "inline;"
+ , try $ caseInsensitiveString "cdecl;"
+ , try $ string "overload;"
+ , try $ string "export;"
+ , try $ string "varargs;"
+ , try (string "external") >> comments >> iD >> optional (string "name" >> comments >> stringLiteral pas)>> string ";"
+ ]
+ comments
+ return d
program = do
@@ -348,36 +351,46 @@
comments
return $ Implementation u (TypesAndVars tv)
-expression = buildExpressionParser table term <?> "expression"
+expression = do
+ buildExpressionParser table term <?> "expression"
where
term = comments >> choice [
builtInFunction expression >>= \(n, e) -> return $ BuiltInFunCall e (SimpleReference (Identifier n BTUnknown))
, try (parens pas $ expression >>= \e -> notFollowedBy (comments >> char '.') >> return e)
, brackets pas (commaSep pas iD) >>= return . SetExpression
- , try $ natural pas >>= \i -> notFollowedBy (char '.') >> (return . NumberLiteral . show) i
+ , try $ integer pas >>= \i -> notFollowedBy (char '.') >> (return . NumberLiteral . show) i
, float pas >>= return . FloatLiteral . show
- , natural pas >>= return . NumberLiteral . show
+ , try $ integer pas >>= return . NumberLiteral . show
, try (string "_S" >> stringLiteral pas) >>= return . StringLiteral
, try (string "_P" >> stringLiteral pas) >>= return . PCharLiteral
, stringLiteral pas >>= return . strOrChar
, try (string "#$") >> many hexDigit >>= \c -> comments >> return (HexCharCode c)
, char '#' >> many digit >>= \c -> comments >> return (CharCode c)
, char '$' >> many hexDigit >>= \h -> comments >> return (HexNumber h)
- , char '-' >> expression >>= return . PrefixOp "-"
+ --, char '-' >> expression >>= return . PrefixOp "-"
+ , char '-' >> reference >>= return . PrefixOp "-" . Reference
+ , try $ string "not" >> error "unexpected not in term"
, try $ string "nil" >> return Null
- , try $ string "not" >> expression >>= return . PrefixOp "not"
, reference >>= return . Reference
] <?> "simple expression"
- table = [
+ table = [
+ [ Prefix (try (string "not") >> return (PrefixOp "not"))
+ , Prefix (try (char '-') >> return (PrefixOp "-"))]
+ ,
[ Infix (char '*' >> return (BinOp "*")) AssocLeft
, Infix (char '/' >> return (BinOp "/")) AssocLeft
, Infix (try (string "div") >> return (BinOp "div")) AssocLeft
, Infix (try (string "mod") >> return (BinOp "mod")) AssocLeft
, Infix (try (string "in") >> return (BinOp "in")) AssocNone
+ , Infix (try $ string "and" >> return (BinOp "and")) AssocLeft
+ , Infix (try $ string "shl" >> return (BinOp "shl")) AssocLeft
+ , Infix (try $ string "shr" >> return (BinOp "shr")) AssocLeft
]
, [ Infix (char '+' >> return (BinOp "+")) AssocLeft
, Infix (char '-' >> return (BinOp "-")) AssocLeft
+ , Infix (try $ string "or" >> return (BinOp "or")) AssocLeft
+ , Infix (try $ string "xor" >> return (BinOp "xor")) AssocLeft
]
, [ Infix (try (string "<>") >> return (BinOp "<>")) AssocNone
, Infix (try (string "<=") >> return (BinOp "<=")) AssocNone
@@ -385,13 +398,13 @@
, Infix (char '<' >> return (BinOp "<")) AssocNone
, Infix (char '>' >> return (BinOp ">")) AssocNone
]
- , [ Infix (try $ string "shl" >> return (BinOp "shl")) AssocNone
- , Infix (try $ string "shr" >> return (BinOp "shr")) AssocNone
+ {-, [ Infix (try $ string "shl" >> return (BinOp "shl")) AssocNone
+ , Infix (try $ string "shr" >> return (BinOp "shr")) AssocNone
]
- , [ Infix (try $ string "and" >> return (BinOp "and")) AssocLeft
- , Infix (try $ string "or" >> return (BinOp "or")) AssocLeft
+ , [
+ Infix (try $ string "or" >> return (BinOp "or")) AssocLeft
, Infix (try $ string "xor" >> return (BinOp "xor")) AssocLeft
- ]
+ ]-}
, [
Infix (char '=' >> return (BinOp "=")) AssocNone
]
@@ -415,7 +428,7 @@
, switchCase
, withBlock
, forCycle
- , (try $ reference >>= \r -> string ":=" >> return r) >>= \r -> expression >>= return . Assignment r
+ , (try $ reference >>= \r -> string ":=" >> return r) >>= \r -> comments >> expression >>= return . Assignment r
, builtInFunction expression >>= \(n, e) -> return $ BuiltInFunctionCall e (SimpleReference (Identifier n BTUnknown))
, procCall
, char ';' >> comments >> return NOP
@@ -480,7 +493,12 @@
comments
e1 <- expression
comments
- choice [string "to", string "downto"]
+ up <- liftM (== Just "to") $
+ optionMaybe $ choice [
+ try $ string "to"
+ , try $ string "downto"
+ ]
+ --choice [string "to", string "downto"]
comments
e2 <- expression
comments
@@ -488,7 +506,7 @@
comments
p <- phrase
comments
- return $ ForCycle i e1 e2 p
+ return $ ForCycle i e1 e2 p up
switchCase = do
try $ string "case"
@@ -573,14 +591,20 @@
table = [
[
Prefix (char '-' >> return (InitPrefixOp "-"))
+ ,Prefix (try (string "not") >> return (InitPrefixOp "not"))
]
, [ Infix (char '*' >> return (InitBinOp "*")) AssocLeft
, Infix (char '/' >> return (InitBinOp "/")) AssocLeft
, Infix (try (string "div") >> return (InitBinOp "div")) AssocLeft
, Infix (try (string "mod") >> return (InitBinOp "mod")) AssocLeft
+ , Infix (try $ string "and" >> return (InitBinOp "and")) AssocLeft
+ , Infix (try $ string "shl" >> return (InitBinOp "shl")) AssocNone
+ , Infix (try $ string "shr" >> return (InitBinOp "shr")) AssocNone
]
, [ Infix (char '+' >> return (InitBinOp "+")) AssocLeft
, Infix (char '-' >> return (InitBinOp "-")) AssocLeft
+ , Infix (try $ string "or" >> return (InitBinOp "or")) AssocLeft
+ , Infix (try $ string "xor" >> return (InitBinOp "xor")) AssocLeft
]
, [ Infix (try (string "<>") >> return (InitBinOp "<>")) AssocNone
, Infix (try (string "<=") >> return (InitBinOp "<=")) AssocNone
@@ -589,14 +613,14 @@
, Infix (char '>' >> return (InitBinOp ">")) AssocNone
, Infix (char '=' >> return (InitBinOp "=")) AssocNone
]
- , [ Infix (try $ string "and" >> return (InitBinOp "and")) AssocLeft
+ {--, [ Infix (try $ string "and" >> return (InitBinOp "and")) AssocLeft
, Infix (try $ string "or" >> return (InitBinOp "or")) AssocLeft
, Infix (try $ string "xor" >> return (InitBinOp "xor")) AssocLeft
]
, [ Infix (try $ string "shl" >> return (InitBinOp "shl")) AssocNone
, Infix (try $ string "shr" >> return (InitBinOp "shr")) AssocNone
- ]
- , [Prefix (try (string "not") >> return (InitPrefixOp "not"))]
+ ]--}
+ --, [Prefix (try (string "not") >> return (InitPrefixOp "not"))]
]
itypeCast = do
@@ -621,3 +645,14 @@
string "var"
v <- varsDecl True
return $ System (t ++ v)
+
+redoUnit = do
+ string "redo;"
+ comments
+ string "type"
+ comments
+ t <- typesDecl
+ string "var"
+ v <- varsDecl True
+ return $ Redo (t ++ v)
+
--- a/tools/PascalPreprocessor.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/PascalPreprocessor.hs Thu Aug 30 13:02:19 2012 -0400
@@ -18,6 +18,8 @@
initDefines = Map.fromList [
("FPC", "")
, ("PAS2C", "")
+ , ("ENDIAN_LITTLE", "")
+ , ("S3D_DISABLED", "")
]
preprocess :: String -> IO String
--- a/tools/PascalUnitSyntaxTree.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/PascalUnitSyntaxTree.hs Thu Aug 30 13:02:19 2012 -0400
@@ -7,6 +7,7 @@
Program Identifier Implementation Phrase
| Unit Identifier Interface Implementation (Maybe Initialize) (Maybe Finalize)
| System [TypeVarDeclaration]
+ | Redo [TypeVarDeclaration]
deriving Show
data Interface = Interface Uses TypesAndVars
deriving Show
@@ -18,8 +19,8 @@
deriving Show
data TypeVarDeclaration = TypeDeclaration Identifier TypeDecl
| VarDeclaration Bool Bool ([Identifier], TypeDecl) (Maybe InitExpression)
- | FunctionDeclaration Identifier TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
- | OperatorDeclaration String Identifier TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
+ | FunctionDeclaration Identifier Bool TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
+ | OperatorDeclaration String Identifier Bool TypeDecl [TypeVarDeclaration] (Maybe (TypesAndVars, Phrase))
deriving Show
data TypeDecl = SimpleType Identifier
| RangeType Range
@@ -48,7 +49,7 @@
| IfThenElse Expression Phrase (Maybe Phrase)
| WhileCycle Expression Phrase
| RepeatCycle Expression [Phrase]
- | ForCycle Identifier Expression Expression Phrase
+ | ForCycle Identifier Expression Expression Phrase Bool -- The last Boolean indicates wether it's up or down counting
| WithBlock Reference Phrase
| Phrases [Phrase]
| SwitchCase Expression [([InitExpression], Phrase)] (Maybe [Phrase])
--- a/tools/pas2c.hs Thu Aug 30 12:47:41 2012 -0400
+++ b/tools/pas2c.hs Thu Aug 30 13:02:19 2012 -0400
@@ -17,24 +17,32 @@
import Data.List (find)
import Numeric
-import PascalParser
+import PascalParser(pascalUnit)
import PascalUnitSyntaxTree
data InsertOption =
IOInsert
+ | IOInsertWithType Doc
| IOLookup
| IOLookupLast
| IOLookupFunction Int
| IODeferred
-type Record = (String, BaseType)
+data Record = Record
+ {
+ lcaseId :: String,
+ baseType :: BaseType,
+ typeDecl :: Doc
+ }
+ deriving Show
type Records = Map.Map String [Record]
data RenderState = RenderState
{
currentScope :: Records,
lastIdentifier :: String,
lastType :: BaseType,
+ lastIdTypeDecl :: Doc,
stringConsts :: [(String, String)],
uniqCounter :: Int,
toMangle :: Set.Set String,
@@ -43,7 +51,9 @@
namespaces :: Map.Map String Records
}
-emptyState = RenderState Map.empty "" BTUnknown [] 0 Set.empty "" ""
+rec2Records = map (\(a, b) -> Record a b empty)
+
+emptyState = RenderState Map.empty "" BTUnknown empty [] 0 Set.empty "" ""
getUniq :: State RenderState Int
getUniq = do
@@ -71,13 +81,14 @@
escapeChar :: Char -> ShowS
escapeChar '"' s = "\\\"" ++ s
+escapeChar '\\' s = "\\\\" ++ s
escapeChar a s = a : s
strInit :: String -> Doc
strInit a = text "STRINIT" <> parens (doubleQuotes (text $ escapeStr a))
renderStringConsts :: State RenderState Doc
-renderStringConsts = liftM (vcat . map (\(a, b) -> text "const string255" <+> (text a) <+> text "=" <+> strInit b <> semi))
+renderStringConsts = liftM (vcat . map (\(a, b) -> text "static const string255" <+> (text a) <+> text "=" <+> strInit b <> semi))
$ gets stringConsts
docToLower :: Doc -> Doc
@@ -132,10 +143,16 @@
where
f = do
checkDuplicateFunDecls tvs
- mapM_ (tvar2C True) tvs
+ mapM_ (tvar2C True False True False) tvs
+ toNamespace nss (Redo tvs) = -- functions that are re-implemented, add prefix to all of them
+ currentScope $ execState f (emptyState nss){currentUnit = "fpcrtl_"}
+ where
+ f = do
+ checkDuplicateFunDecls tvs
+ mapM_ (tvar2C True False True False) tvs
toNamespace _ (Program {}) = Map.empty
toNamespace nss (Unit (Identifier i _) interface _ _ _) =
- currentScope $ execState (interface2C interface) (emptyState nss){currentUnit = map toLower i ++ "_"}
+ currentScope $ execState (interface2C interface True) (emptyState nss){currentUnit = map toLower i ++ "_"}
withState' :: (RenderState -> RenderState) -> State RenderState a -> State RenderState a
@@ -149,65 +166,72 @@
})
return a
-withLastIdNamespace :: State RenderState Doc -> State RenderState Doc
withLastIdNamespace f = do
li <- gets lastIdentifier
nss <- gets namespaces
withState' (\st -> st{currentScope = fromMaybe Map.empty $ Map.lookup li (namespaces st)}) f
-withRecordNamespace :: String -> [(String, BaseType)] -> State RenderState Doc -> State RenderState Doc
+withRecordNamespace :: String -> [Record] -> State RenderState Doc -> State RenderState Doc
withRecordNamespace _ [] = error "withRecordNamespace: empty record"
withRecordNamespace prefix recs = withState' f
where
f st = st{currentScope = Map.unionWith un records (currentScope st), currentUnit = ""}
- records = Map.fromList $ map (\(a, b) -> (map toLower a, [(prefix ++ a, b)])) recs
+ records = Map.fromList $ map (\(Record a b d) -> (map toLower a, [Record (prefix ++ a) b d])) recs
un [a] b = a : b
toCFiles :: Map.Map String Records -> (String, PascalUnit) -> IO ()
toCFiles _ (_, System _) = return ()
+toCFiles _ (_, Redo _) = return ()
toCFiles ns p@(fn, pu) = do
hPutStrLn stdout $ "Rendering '" ++ fn ++ "'..."
toCFiles' p
where
- toCFiles' (fn, p@(Program {})) = writeFile (fn ++ ".c") $ (render2C initialState . pascal2C) p
+ toCFiles' (fn, p@(Program {})) = writeFile (fn ++ ".c") $ "#include \"fpcrtl.h\"\n" ++ (render2C initialState . pascal2C) p
toCFiles' (fn, (Unit unitId@(Identifier i _) interface implementation _ _)) = do
- let (a, s) = runState (id2C IOInsert (setBaseType BTUnit unitId) >> interface2C interface) initialState{currentUnit = map toLower i ++ "_"}
+ let (a, s) = runState (id2C IOInsert (setBaseType BTUnit unitId) >> interface2C interface True) initialState{currentUnit = map toLower i ++ "_"}
+ (a', s') = runState (id2C IOInsert (setBaseType BTUnit unitId) >> interface2C interface False) initialState{currentUnit = map toLower i ++ "_"}
writeFile (fn ++ ".h") $ "#pragma once\n\n#include \"pas2c.h\"\n\n" ++ (render (a $+$ text ""))
- writeFile (fn ++ ".c") $ "#include \"" ++ fn ++ ".h\"\n" ++ (render2C s . implementation2C) implementation
+ writeFile (fn ++ ".c") $ "#include \"fpcrtl.h\"\n\n#include \"" ++ fn ++ ".h\"\n" ++ render (a' $+$ text "") ++ (render2C s . implementation2C) implementation
initialState = emptyState ns
render2C :: RenderState -> State RenderState Doc -> String
render2C a = render . ($+$ empty) . flip evalState a
+
usesFiles :: PascalUnit -> [String]
-usesFiles (Program _ (Implementation uses _) _) = "pas2cSystem" : uses2List uses
-usesFiles (Unit _ (Interface uses1 _) (Implementation uses2 _) _ _) = "pas2cSystem" : uses2List uses1 ++ uses2List uses2
+usesFiles (Program _ (Implementation uses _) _) = ["pas2cSystem", "pas2cRedo"] ++ uses2List uses
+usesFiles (Unit _ (Interface uses1 _) (Implementation uses2 _) _ _) = ["pas2cSystem", "pas2cRedo"] ++ uses2List uses1 ++ uses2List uses2
usesFiles (System {}) = []
-
+usesFiles (Redo {}) = []
pascal2C :: PascalUnit -> State RenderState Doc
pascal2C (Unit _ interface implementation init fin) =
- liftM2 ($+$) (interface2C interface) (implementation2C implementation)
+ liftM2 ($+$) (interface2C interface True) (implementation2C implementation)
pascal2C (Program _ implementation mainFunction) = do
impl <- implementation2C implementation
- [main] <- tvar2C True
- (FunctionDeclaration (Identifier "main" BTInt) (SimpleType $ Identifier "int" BTInt) [] (Just (TypesAndVars [], mainFunction)))
+ [main] <- tvar2C True False True True (FunctionDeclaration (Identifier "main" BTInt) False (SimpleType $ Identifier "int" BTInt) [VarDeclaration False False ([Identifier "argc" BTInt], SimpleType (Identifier "Integer" BTInt)) Nothing, VarDeclaration False False ([Identifier "argv" BTUnknown], SimpleType (Identifier "PPChar" BTUnknown)) Nothing] (Just (TypesAndVars [], mainFunction)))
return $ impl $+$ main
-
-interface2C :: Interface -> State RenderState Doc
-interface2C (Interface uses tvars) = do
+-- the second bool indicates whether do normal interface translation or generate variable declarations
+-- that will be inserted into implementation files
+interface2C :: Interface -> Bool -> State RenderState Doc
+interface2C (Interface uses tvars) True = do
u <- uses2C uses
- tv <- typesAndVars2C True tvars
+ tv <- typesAndVars2C True True True tvars
r <- renderStringConsts
return (u $+$ r $+$ tv)
+interface2C (Interface uses tvars) False = do
+ u <- uses2C uses
+ tv <- typesAndVars2C True False False tvars
+ r <- renderStringConsts
+ return tv
implementation2C :: Implementation -> State RenderState Doc
implementation2C (Implementation uses tvars) = do
u <- uses2C uses
- tv <- typesAndVars2C True tvars
+ tv <- typesAndVars2C True False True tvars
r <- renderStringConsts
return (u $+$ r $+$ tv)
@@ -217,20 +241,25 @@
where
initMap = Map.empty
--initMap = Map.fromList [("reset", 2)]
- ins (FunctionDeclaration (Identifier i _) _ _ _) m = Map.insertWith (+) (map toLower i) 1 m
+ ins (FunctionDeclaration (Identifier i _) _ _ _ _) m = Map.insertWith (+) (map toLower i) 1 m
ins _ m = m
-typesAndVars2C :: Bool -> TypesAndVars -> State RenderState Doc
-typesAndVars2C b (TypesAndVars ts) = do
+-- the second bool indicates whether declare variable as extern or not
+-- the third bool indicates whether include types or not
+
+typesAndVars2C :: Bool -> Bool -> Bool -> TypesAndVars -> State RenderState Doc
+typesAndVars2C b externVar includeType(TypesAndVars ts) = do
checkDuplicateFunDecls ts
- liftM (vcat . map (<> semi) . concat) $ mapM (tvar2C b) ts
+ liftM (vcat . map (<> semi) . concat) $ mapM (tvar2C b externVar includeType False) ts
setBaseType :: BaseType -> Identifier -> Identifier
setBaseType bt (Identifier i _) = Identifier i bt
uses2C :: Uses -> State RenderState Doc
uses2C uses@(Uses unitIds) = do
+
mapM_ injectNamespace (Identifier "pas2cSystem" undefined : unitIds)
+ mapM_ injectNamespace (Identifier "pas2cRedo" undefined : unitIds)
mapM_ (id2C IOInsert . setBaseType BTUnit) unitIds
return $ vcat . map (\i -> text $ "#include \"" ++ i ++ ".h\"") $ uses2List uses
where
@@ -242,8 +271,11 @@
uses2List (Uses ids) = map (\(Identifier i _) -> i) ids
+setLastIdValues vv = (\s -> s{lastType = baseType vv, lastIdentifier = lcaseId vv, lastIdTypeDecl = typeDecl vv})
+
id2C :: InsertOption -> Identifier -> State RenderState Doc
-id2C IOInsert (Identifier i t) = do
+id2C IOInsert i = id2C (IOInsertWithType empty) i
+id2C (IOInsertWithType d) (Identifier i t) = do
ns <- gets currentScope
tom <- gets (Set.member n . toMangle)
cu <- gets currentUnit
@@ -252,10 +284,11 @@
(BTFunction _ _ _, _) -> (cu ++ i, t)
(BTVarParam t', _) -> ('(' : '*' : i ++ ")" , t')
_ -> (i, t)
- modify (\s -> s{currentScope = Map.insertWith (++) n [(i', t')] (currentScope s), lastIdentifier = n})
+ modify (\s -> s{currentScope = Map.insertWith (++) n [Record i' t' d] (currentScope s), lastIdentifier = n})
return $ text i'
where
n = map toLower i
+
id2C IOLookup i = id2CLookup head i
id2C IOLookupLast i = id2CLookup last i
id2C (IOLookupFunction params) (Identifier i t) = do
@@ -266,9 +299,9 @@
error $ "Not defined: '" ++ i' ++ "'\n" ++ show lt ++ "\nwith num of params = " ++ show params ++ "\n" ++ show v
else
let vv = fromMaybe (head $ fromJust v) . find checkParam $ fromJust v in
- modify (\s -> s{lastType = snd vv, lastIdentifier = fst vv}) >> (return . text . fst $ vv)
+ modify (setLastIdValues vv) >> (return . text . lcaseId $ vv)
where
- checkParam (_, BTFunction _ p _) = p == params
+ checkParam (Record _ (BTFunction _ p _) _) = p == params
checkParam _ = False
id2C IODeferred (Identifier i t) = do
let i' = map toLower i
@@ -276,40 +309,44 @@
if (isNothing v) then
modify (\s -> s{lastType = BTUnknown, lastIdentifier = i}) >> return (text i)
else
- let vv = head $ fromJust v in modify (\s -> s{lastType = snd vv, lastIdentifier = fst vv}) >> (return . text . fst $ vv)
+ let vv = head $ fromJust v in modify (setLastIdValues vv) >> (return . text . lcaseId $ vv)
id2CLookup :: ([Record] -> Record) -> Identifier -> State RenderState Doc
-id2CLookup f (Identifier i _) = do
+id2CLookup f (Identifier i t) = do
let i' = map toLower i
v <- gets $ Map.lookup i' . currentScope
lt <- gets lastType
if isNothing v then
error $ "Not defined: '" ++ i' ++ "'\n" ++ show lt
else
- let vv = f $ fromJust v in modify (\s -> s{lastType = snd vv, lastIdentifier = fst vv}) >> (return . text . fst $ vv)
+ let vv = f $ fromJust v in modify (setLastIdValues vv) >> (return . text . lcaseId $ vv)
id2CTyped :: TypeDecl -> Identifier -> State RenderState Doc
-id2CTyped t (Identifier i _) = do
+id2CTyped = id2CTyped2 Nothing
+
+id2CTyped2 :: Maybe Doc -> TypeDecl -> Identifier -> State RenderState Doc
+id2CTyped2 md t (Identifier i _) = do
tb <- resolveType t
case (t, tb) of
(_, BTUnknown) -> do
error $ "id2CTyped: type BTUnknown for " ++ show i ++ "\ntype: " ++ show t
(SimpleType {}, BTRecord _ r) -> do
ts <- type2C t
- id2C IOInsert (Identifier i (BTRecord (render $ ts empty) r))
+ id2C (IOInsertWithType $ ts empty) (Identifier i (BTRecord (render $ ts empty) r))
(_, BTRecord _ r) -> do
ts <- type2C t
- id2C IOInsert (Identifier i (BTRecord i r))
- _ -> id2C IOInsert (Identifier i tb)
-
+ id2C (IOInsertWithType $ ts empty) (Identifier i (BTRecord i r))
+ _ -> case md of
+ Nothing -> id2C IOInsert (Identifier i tb)
+ Just ts -> id2C (IOInsertWithType ts) (Identifier i tb)
resolveType :: TypeDecl -> State RenderState BaseType
resolveType st@(SimpleType (Identifier i _)) = do
let i' = map toLower i
v <- gets $ Map.lookup i' . currentScope
- if isJust v then return . snd . head $ fromJust v else return $ f i'
+ if isJust v then return . baseType . head $ fromJust v else return $ f i'
where
f "integer" = BTInt
f "pointer" = BTPointerTo BTVoid
@@ -352,7 +389,7 @@
resolve s (BTUnresolved t) = do
v <- gets $ Map.lookup t . currentScope
if isJust v then
- resolve s . snd . head . fromJust $ v
+ resolve s . baseType . head . fromJust $ v
else
error $ "Unknown type " ++ show t ++ "\n" ++ s
resolve _ t = return t
@@ -363,7 +400,7 @@
error $ "Dereferencing from non-pointer type " ++ show t ++ "\n" ++ s
-functionParams2C params = liftM (hcat . punctuate comma . concat) $ mapM (tvar2C False) params
+functionParams2C params = liftM (hcat . punctuate comma . concat) $ mapM (tvar2C False False True True) params
numberOfDeclarations :: [TypeVarDeclaration] -> Int
numberOfDeclarations = sum . map cnt
@@ -392,20 +429,21 @@
ps = zip ['a'..] (toIsVarList params)
fun2C :: Bool -> String -> TypeVarDeclaration -> State RenderState [Doc]
-fun2C _ _ (FunctionDeclaration name returnType params Nothing) = do
+fun2C _ _ (FunctionDeclaration name inline returnType params Nothing) = do
t <- type2C returnType
t'<- gets lastType
p <- withState' id $ functionParams2C params
n <- liftM render . id2C IOInsert $ setBaseType (BTFunction hasVars (numberOfDeclarations params) t') name
+ let decor = if inline then text "inline" else empty
if hasVars then
- return [funWithVarsToDefine n params $+$ t empty <+> text (n ++ "__vars") <> parens p]
+ return [funWithVarsToDefine n params $+$ decor <+> t empty <+> text (n ++ "__vars") <> parens p]
else
- return [t empty <+> text n <> parens p]
+ return [decor <+> t empty <+> text n <> parens p]
where
hasVars = hasPassByReference params
-fun2C True rv (FunctionDeclaration name@(Identifier i _) returnType params (Just (tvars, phrase))) = do
+fun2C True rv (FunctionDeclaration name@(Identifier i _) inline returnType params (Just (tvars, phrase))) = do
let res = docToLower $ text rv <> text "_result"
t <- type2C returnType
t'<- gets lastType
@@ -418,16 +456,20 @@
VoidType -> True
_ -> False
- (p, ph) <- withState' (\st -> st{currentScope = Map.insertWith un (map toLower rv) [(render res, t')] $ currentScope st
+ (p, ph) <- withState' (\st -> st{currentScope = Map.insertWith un (map toLower rv) [Record (render res) t' empty] $ currentScope st
, currentFunctionResult = if isVoid then [] else render res}) $ do
p <- functionParams2C params
- ph <- liftM2 ($+$) (typesAndVars2C False tvars) (phrase2C' phrase)
+ ph <- liftM2 ($+$) (typesAndVars2C False False True tvars) (phrase2C' phrase)
return (p, ph)
let phrasesBlock = if isVoid then ph else t empty <+> res <> semi $+$ ph $+$ text "return" <+> res <> semi
-
- return [(if notDeclared && hasVars then funWithVarsToDefine n params else empty) $+$
- t empty <+> text (if hasVars then n ++ "__vars" else n) <> parens p
+ let define = if hasVars then text "#ifndef" <+> text n $+$ funWithVarsToDefine n params $+$ text "#endif" else empty
+ let decor = if inline then text "inline" else empty
+ return [
+ define
+ $+$
+ --(if notDeclared && hasVars then funWithVarsToDefine n params else empty) $+$
+ decor <+> t empty <+> text (if hasVars then n ++ "__vars" else n) <> parens p
$+$
text "{"
$+$
@@ -440,42 +482,74 @@
un [a] b = a : b
hasVars = hasPassByReference params
-fun2C False _ (FunctionDeclaration (Identifier name _) _ _ _) = error $ "nested functions not allowed: " ++ name
+fun2C False _ (FunctionDeclaration (Identifier name _) _ _ _ _) = error $ "nested functions not allowed: " ++ name
fun2C _ tv _ = error $ "fun2C: I don't render " ++ show tv
-tvar2C :: Bool -> TypeVarDeclaration -> State RenderState [Doc]
-tvar2C b f@(FunctionDeclaration (Identifier name _) _ _ _) =
- fun2C b name f
-tvar2C _ td@(TypeDeclaration i' t) = do
+-- the second bool indicates whether declare variable as extern or not
+-- the third bool indicates whether include types or not
+-- the fourth bool indicates whether ignore initialization or not (basically for dynamic arrays since we cannot do initialization in function params)
+tvar2C :: Bool -> Bool -> Bool -> Bool -> TypeVarDeclaration -> State RenderState [Doc]
+tvar2C b _ includeType _ f@(FunctionDeclaration (Identifier name _) _ _ _ _) = do
+ t <- fun2C b name f
+ if includeType then return t else return []
+tvar2C _ _ includeType _ td@(TypeDeclaration i' t) = do
i <- id2CTyped t i'
tp <- type2C t
- return [text "typedef" <+> tp i]
+ return $ if includeType then [text "typedef" <+> tp i] else []
-tvar2C _ (VarDeclaration True _ (ids, t) Nothing) = do
+tvar2C _ _ _ _ (VarDeclaration True _ (ids, t) Nothing) = do
t' <- liftM ((empty <+>) . ) $ type2C t
- liftM (map(\i -> t' i)) $ mapM (id2CTyped (VarParamType t)) ids
+ liftM (map(\i -> t' i)) $ mapM (id2CTyped2 (Just $ t' empty) (VarParamType t)) ids
-tvar2C _ (VarDeclaration _ isConst (ids, t) mInitExpr) = do
- t' <- liftM (((if isConst then text "const" else empty) <+>) . ) $ type2C t
+tvar2C _ externVar includeType ignoreInit (VarDeclaration _ isConst (ids, t) mInitExpr) = do
+ t' <- liftM (((if isConst then text "static const" else if externVar
+ then text "extern"
+ else empty)
+ <+>) . ) $ type2C t
ie <- initExpr mInitExpr
lt <- gets lastType
case (isConst, lt, ids, mInitExpr) of
(True, BTInt, [i], Just _) -> do
i' <- id2CTyped t i
- return [text "enum" <> braces (i' <+> ie)]
+ return $ if includeType then [text "enum" <> braces (i' <+> ie)] else []
(True, BTFloat, [i], Just e) -> do
i' <- id2CTyped t i
ie <- initExpr2C e
- return [text "#define" <+> i' <+> parens ie <> text "\n"]
+ return $ if includeType then [text "#define" <+> i' <+> parens ie <> text "\n"] else []
(_, BTFunction{}, _, Nothing) -> liftM (map(\i -> t' i)) $ mapM (id2CTyped t) ids
- _ -> liftM (map(\i -> t' i <+> ie)) $ mapM (id2CTyped t) ids
+ (_, BTArray r _ _, [i], _) -> do
+ i' <- id2CTyped t i
+ ie' <- return $ case (r, mInitExpr, ignoreInit) of
+ (RangeInfinite, Nothing, False) -> text "= NULL" -- force dynamic array to be initialized as NULL if not initialized at all
+ (_, _, _) -> ie
+ result <- liftM (map(\i -> varDeclDecision isConst includeType (t' i) ie')) $ mapM (id2CTyped t) ids
+ case (r, ignoreInit) of
+ (RangeInfinite, False) ->
+ -- if the array is dynamic, add dimension info to it
+ return $ [dimDecl] ++ result
+ where
+ arrayDimStr = show $ arrayDimension t
+ arrayDimInitExp = text ("={" ++ ".dim = " ++ arrayDimStr ++ ", .a = {0, 0, 0, 0}}")
+ dimDecl = varDeclDecision isConst includeType (text "fpcrtl_dimension_t" <+> i' <> text "_dimension_info") arrayDimInitExp
+
+ (_, _) -> return result
+
+ _ -> liftM (map(\i -> varDeclDecision isConst includeType (t' i) ie)) $ mapM (id2CTyped2 (Just $ t' empty) t) ids
where
initExpr Nothing = return $ empty
initExpr (Just e) = liftM (text "=" <+>) (initExpr2C e)
+ varDeclDecision True True varStr expStr = varStr <+> expStr
+ varDeclDecision False True varStr expStr = if externVar then varStr else varStr <+> expStr
+ varDeclDecision False False varStr expStr = varStr <+> expStr
+ varDeclDecision True False varStr expStr = empty
+ arrayDimension a = case a of
+ ArrayDecl Nothing t -> let a = arrayDimension t in if a > 3 then error "Dynamic array with dimension > 4 is not supported." else 1 + arrayDimension t
+ ArrayDecl _ _ -> error "Mixed dynamic array and static array are not supported."
+ _ -> 0
-tvar2C f (OperatorDeclaration op (Identifier i _) ret params body) = do
+tvar2C f _ _ _ (OperatorDeclaration op (Identifier i _) inline ret params body) = do
r <- op2CTyped op (extractTypes params)
- fun2C f i (FunctionDeclaration r ret params body)
+ fun2C f i (FunctionDeclaration r inline ret params body)
op2CTyped :: String -> [TypeDecl] -> State RenderState Identifier
@@ -489,6 +563,7 @@
"-" -> "sub"
"*" -> "mul"
"/" -> "div"
+ "/(float)" -> "div"
"=" -> "eq"
"<" -> "lt"
">" -> "gt"
@@ -591,7 +666,7 @@
_ -> return $ \a -> i' <+> text "*" <+> a
type2C' (PointerTo t) = liftM (\t a -> t (parens $ text "*" <> a)) $ type2C t
type2C' (RecordType tvs union) = do
- t <- withState' f $ mapM (tvar2C False) tvs
+ t <- withState' f $ mapM (tvar2C False False True False) tvs
u <- unions
return $ \i -> text "struct __" <> i <+> lbrace $+$ nest 4 ((vcat . map (<> semi) . concat $ t) $$ u) $+$ rbrace <+> i
where
@@ -602,7 +677,7 @@
structs <- mapM struct2C a
return $ text "union" $+$ braces (nest 4 $ vcat structs) <> semi
struct2C tvs = do
- t <- withState' f $ mapM (tvar2C False) tvs
+ t <- withState' f $ mapM (tvar2C False False True False) tvs
return $ text "struct" $+$ braces (nest 4 (vcat . map (<> semi) . concat $ t)) <> semi
type2C' (RangeType r) = return (text "int" <+>)
type2C' (Sequence ids) = do
@@ -615,7 +690,7 @@
t' <- type2C t
lt <- gets lastType
ft <- case lt of
- BTFunction {} -> type2C (PointerTo t)
+ -- BTFunction {} -> type2C (PointerTo t)
_ -> return t'
r' <- initExpr2C (InitRange r)
return $ \i -> ft i <> brackets r'
@@ -675,15 +750,26 @@
e <- expr2C expr
return $ r <+> text "=" <+> e <> semi
_ -> error $ "Assignment to string from " ++ show lt
- (BTArray _ _ _, _) -> phrase2C $
- ProcCall (FunCall
- [
- Reference $ Address ref
- , Reference $ Address $ RefExpression expr
- , Reference $ FunCall [expr] (SimpleReference (Identifier "sizeof" BTUnknown))
- ]
- (SimpleReference (Identifier "memcpy" BTUnknown))
- ) []
+ (BTArray _ _ _, _) -> do
+ case expr of
+ Reference er -> do
+ exprRef <- ref2C er
+ exprT <- gets lastType
+ case exprT of
+ BTArray RangeInfinite _ _ ->
+ return $ text "FIXME: assign a dynamic array to an array"
+ BTArray _ _ _ -> phrase2C $
+ ProcCall (FunCall
+ [
+ Reference $ ref
+ , Reference $ RefExpression expr
+ , Reference $ FunCall [expr] (SimpleReference (Identifier "sizeof" BTUnknown))
+ ]
+ (SimpleReference (Identifier "memcpy" BTUnknown))
+ ) []
+ _ -> return $ text "FIXME: assign a non-specific value to an array"
+
+ _ -> return $ text "FIXME: dynamic array assignment 2"
_ -> do
e <- expr2C expr
return $ r <+> text "=" <+> e <> semi
@@ -704,7 +790,7 @@
ph <- phrase2C p
return $
vcat (map (\i -> text "case" <+> i <> colon) . concat $ ies) <> nest 4 (ph $+$ text "break;")
- dflt | isNothing mphrase = return []
+ dflt | isNothing mphrase = return [text "default: break;"] -- avoid compiler warning
| otherwise = do
ph <- mapM phrase2C $ fromJust mphrase
return [text "default:" <+> nest 4 (vcat ph)]
@@ -713,18 +799,27 @@
r <- ref2C ref
t <- gets lastType
case t of
- (BTRecord _ rs) -> withRecordNamespace (render r ++ ".") rs $ phrase2C $ wrapPhrase p
+ (BTRecord _ rs) -> withRecordNamespace (render r ++ ".") (rec2Records rs) $ phrase2C $ wrapPhrase p
a -> do
error $ "'with' block referencing non-record type " ++ show a ++ "\n" ++ show wb
-phrase2C (ForCycle i' e1' e2' p) = do
+phrase2C (ForCycle i' e1' e2' p up) = do
i <- id2C IOLookup i'
+ iType <- gets lastIdTypeDecl
e1 <- expr2C e1'
e2 <- expr2C e2'
- ph <- phrase2C (wrapPhrase p)
- return $
- text "for" <> (parens . hsep . punctuate (char ';') $ [i <+> text "=" <+> e1, i <+> text "<=" <+> e2, text "++" <> i])
+ let inc = if up then "inc" else "dec"
+ let add = if up then "+ 1" else "- 1"
+ let iEnd = i <> text "__end__"
+ ph <- phrase2C . appendPhrase (BuiltInFunctionCall [Reference $ SimpleReference i'] (SimpleReference (Identifier inc BTUnknown))) $ wrapPhrase p
+ return . braces $
+ i <+> text "=" <+> e1 <> semi
$$
- ph
+ iType <+> iEnd <+> text "=" <+> e2 <> semi
+ $$
+ text "if" <+> (parens $ i <+> text "<=" <+> iEnd) <+> text "do" <+> ph <+>
+ text "while" <> parens (i <+> text "!=" <+> iEnd <+> text add) <> semi
+ where
+ appendPhrase p (Phrases ps) = Phrases $ ps ++ [p]
phrase2C (RepeatCycle e' p') = do
e <- expr2C e'
p <- phrase2C (Phrases p')
@@ -777,12 +872,23 @@
case expr2 of
SetExpression set -> do
ids <- mapM (id2C IOLookup) set
+ modify(\s -> s{lastType = BTBool})
return . parens . hcat . punctuate (text " || ") . map (\i -> parens $ e1 <+> text "==" <+> i) $ ids
_ -> error "'in' against not set expression"
(o, _, _) | o `elem` boolOps -> do
modify(\s -> s{lastType = BTBool})
return $ parens e1 <+> text o <+> parens e2
- | otherwise -> return $ parens e1 <+> text o <+> parens e2
+ | otherwise -> do
+ o' <- return $ case o of
+ "/(float)" -> text "/(float)" -- pascal returns real value
+ _ -> text o
+ e1' <- return $ case (o, t1, t2) of
+ ("-", BTInt, BTInt) -> parens $ text "(int64_t)" <+> parens e1
+ _ -> parens e1
+ e2' <- return $ case (o, t1, t2) of
+ ("-", BTInt, BTInt) -> parens $ text "(int64_t)" <+> parens e2
+ _ -> parens e2
+ return $ e1' <+> o' <+> e2'
where
boolOps = ["==", "!=", "<", ">", "<=", ">="]
expr2C (NumberLiteral s) = do
@@ -806,7 +912,12 @@
BTRecord t _ -> do
i <- op2CTyped op [SimpleType (Identifier t undefined)]
ref2C $ FunCall [expr] (SimpleReference i)
- _ -> return $ text (op2C op) <> e
+ BTBool -> do
+ o <- return $ case op of
+ "not" -> text "!"
+ _ -> text (op2C op)
+ return $ o <> parens e
+ _ -> return $ text (op2C op) <> parens e
expr2C Null = return $ text "NULL"
expr2C (CharCode a) = do
modify(\s -> s{lastType = BTChar})
@@ -835,13 +946,13 @@
_ -> error $ "BuiltInFunCall 'high' from " ++ show e ++ "\ntype: " ++ show lt
expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "ord" _))) = liftM parens $ expr2C e
expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "succ" _))) = liftM (<> text " + 1") $ expr2C e
-expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "pred" _))) = liftM (<> text " - 1") $ expr2C e
+expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "pred" _))) = liftM (<> text " - (int64_t)1") $ expr2C e
expr2C (BuiltInFunCall [e] (SimpleReference (Identifier "length" _))) = do
e' <- expr2C e
lt <- gets lastType
modify (\s -> s{lastType = BTInt})
case lt of
- BTString -> return $ text "Length" <> parens e'
+ BTString -> return $ text "fpcrtl_Length" <> parens e'
BTArray RangeInfinite _ _ -> error $ "length() called on variable size array " ++ show e'
BTArray (RangeFromTo _ n) _ _ -> initExpr2C (BuiltInFunction "succ" [n])
_ -> error $ "length() called on " ++ show lt
@@ -864,7 +975,7 @@
case t of
BTFunction _ _ rt -> do
modify(\s -> s{lastType = rt})
- return $ i <> parens empty
+ return $ i <> parens empty --xymeng: removed parens
_ -> return $ i
ref2CF r@(RecordField (SimpleReference _) (SimpleReference _)) = do
i <- ref2C r
@@ -907,7 +1018,7 @@
r1 <- ref2C ref1
t <- fromPointer (show ref1) =<< gets lastType
r2 <- case t of
- BTRecord _ rs -> withRecordNamespace "" rs $ ref2C ref2
+ BTRecord _ rs -> withRecordNamespace "" (rec2Records rs) $ ref2C ref2
BTUnit -> error "What??"
a -> error $ "dereferencing from " ++ show a ++ "\n" ++ show rf
return $
@@ -917,7 +1028,7 @@
t <- gets lastType
case t of
BTRecord _ rs -> do
- r2 <- withRecordNamespace "" rs $ ref2C ref2
+ r2 <- withRecordNamespace "" (rec2Records rs) $ ref2C ref2
return $ r1 <> text "." <> r2
BTUnit -> withLastIdNamespace $ ref2C ref2
a -> error $ "dereferencing from " ++ show a ++ "\n" ++ show rf
@@ -962,7 +1073,7 @@
op2C :: String -> String
op2C "or" = "|"
op2C "and" = "&"
-op2C "not" = "!"
+op2C "not" = "~"
op2C "xor" = "^"
op2C "div" = "/"
op2C "mod" = "%"
@@ -970,5 +1081,6 @@
op2C "shr" = ">>"
op2C "<>" = "!="
op2C "=" = "=="
+op2C "/" = "/(float)"
op2C a = a