# HG changeset patch # User unc0rr # Date 1427998196 -10800 # Node ID 99273b7afbffd48b993c4b3d1554b65eec891ce2 # Parent 1ff3dd3705b1b7eda03d114a11b7184bb6cbb29e# Parent 3ea36d8d46366e257406dbc2b0647d13f9b388e5 - Merge default - *cough* sorry, but also in this commit: move *.qml files into qrc, include qmlFrontend into cmake build, also exclude QTfrontend diff -r 1ff3dd3705b1 -r 99273b7afbff CMakeLists.txt --- a/CMakeLists.txt Mon Feb 16 22:33:15 2015 +0300 +++ b/CMakeLists.txt Thu Apr 02 21:09:56 2015 +0300 @@ -224,7 +224,7 @@ add_subdirectory(project_files/Android-build) else(ANDROID) add_subdirectory(bin) - add_subdirectory(QTfrontend) + add_subdirectory(qmlFrontend) add_subdirectory(share) add_subdirectory(tools) endif(ANDROID) diff -r 1ff3dd3705b1 -r 99273b7afbff QTfrontend/game.cpp --- a/QTfrontend/game.cpp Mon Feb 16 22:33:15 2015 +0300 +++ b/QTfrontend/game.cpp Thu Apr 02 21:09:56 2015 +0300 @@ -16,6 +16,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include +#include + #include #include #include @@ -258,6 +261,18 @@ .arg(QString::fromUtf8(msg.mid(2).left(size - 4)))); return; } + case 'y': + { + // copy string to clipboard + QApplication::clipboard()->setText(QString::fromUtf8(msg.mid(2))); + break; + } + case 'Y': + { + // paste clipboard to game + SendIPC(QString("Y").toAscii() + QApplication::clipboard()->text().toUtf8().left(250).replace('\n', ' ')); + break; + } case 'i': { emit GameStats(msg.at(2), QString::fromUtf8(msg.mid(3))); diff -r 1ff3dd3705b1 -r 99273b7afbff QTfrontend/model/ammoSchemeModel.cpp --- a/QTfrontend/model/ammoSchemeModel.cpp Mon Feb 16 22:33:15 2015 +0300 +++ b/QTfrontend/model/ammoSchemeModel.cpp Thu Apr 02 21:09:56 2015 +0300 @@ -58,14 +58,15 @@ << QVariant(4) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(2) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; AmmoSchemeModel::AmmoSchemeModel(QObject* parent, const QString & fileName) : @@ -125,14 +126,15 @@ << "minesnum" // 32 << "minedudpct" // 33 << "explosives" // 34 - << "healthprobability" // 35 - << "healthcaseamount" // 36 - << "waterrise" // 37 - << "healthdecrease" // 38 - << "ropepct" // 39 - << "getawaytime" // 40 - << "worldedge" // 41 - << "scriptparam" // scriptparam 42 + << "airmines" // 35 + << "healthprobability" // 36 + << "healthcaseamount" // 37 + << "waterrise" // 38 + << "healthdecrease" // 39 + << "ropepct" // 40 + << "getawaytime" // 41 + << "worldedge" // 42 + << "scriptparam" // scriptparam 43 ; QList proMode; @@ -172,14 +174,15 @@ << QVariant(0) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(2) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList shoppa; @@ -215,18 +218,19 @@ << QVariant(100) // init health 28 << QVariant(50) // sudden death 29 << QVariant(1) // case prob 30 - << QVariant(3) // mines time 31 + << QVariant(0) // mines time 31 << QVariant(0) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(0) // explosives 34 - << QVariant(0) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(8) // air mines 35 + << QVariant(0) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList cleanslate; @@ -266,14 +270,15 @@ << QVariant(4) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(2) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList minefield; @@ -313,14 +318,15 @@ << QVariant(200) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(0) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList barrelmayhem; @@ -360,14 +366,15 @@ << QVariant(0) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(200) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList tunnelhogs; @@ -407,14 +414,15 @@ << QVariant(10) // mines number 32 << QVariant(10) // mine dud pct 33 << QVariant(10) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(4) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList forts; @@ -454,14 +462,15 @@ << QVariant(0) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(0) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList timeless; @@ -501,14 +510,15 @@ << QVariant(5) // mines number 32 << QVariant(10) // mine dud pct 33 << QVariant(2) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(30) // health case amt 36 - << QVariant(0) // water rise amt 37 - << QVariant(0) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(30) // health case amt 37 + << QVariant(0) // water rise amt 38 + << QVariant(0) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList thinkingportals; @@ -548,14 +558,15 @@ << QVariant(5) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(5) // explosives 34 - << QVariant(25) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(4) // air mines 35 + << QVariant(25) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList kingmode; @@ -595,14 +606,15 @@ << QVariant(4) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(2) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; QList construction; @@ -642,14 +654,15 @@ << QVariant(0) // mines number 32 << QVariant(0) // mine dud pct 33 << QVariant(0) // explosives 34 - << QVariant(35) // health case pct 35 - << QVariant(25) // health case amt 36 - << QVariant(47) // water rise amt 37 - << QVariant(5) // health dec amt 38 - << QVariant(100) // rope modfier 39 - << QVariant(100) // get away time 40 - << QVariant(0) // world edge 41 - << QVariant() // scriptparam 42 + << QVariant(0) // air mines 35 + << QVariant(35) // health case pct 36 + << QVariant(25) // health case amt 37 + << QVariant(47) // water rise amt 38 + << QVariant(5) // health dec amt 39 + << QVariant(100) // rope modfier 40 + << QVariant(100) // get away time 41 + << QVariant(0) // world edge 42 + << QVariant() // scriptparam 43 ; schemes.append(defaultScheme); @@ -853,7 +866,7 @@ return; } - cfg[42] = cfg[42].mid(1); + cfg[cfg.size()-1] = cfg[cfg.size()-1].mid(1); for(int i = 0; i < cfg.size(); ++i) netScheme[i] = QVariant(cfg[i]); diff -r 1ff3dd3705b1 -r 99273b7afbff QTfrontend/ui/page/pagescheme.cpp --- a/QTfrontend/ui/page/pagescheme.cpp Mon Feb 16 22:33:15 2015 +0300 +++ b/QTfrontend/ui/page/pagescheme.cpp Thu Apr 02 21:09:56 2015 +0300 @@ -369,27 +369,41 @@ glBSLayout->addWidget(SB_Explosives,13,2,1,1); l = new QLabel(gbBasicSettings); - l->setText(QLabel::tr("% Get Away Time")); + l->setText(QLabel::tr("Air Mines")); l->setWordWrap(true); glBSLayout->addWidget(l,14,0,1,1); l = new QLabel(gbBasicSettings); l->setFixedSize(32,32); + l->setPixmap(QPixmap(":/res/iconMine.png")); // TODO: icon + glBSLayout->addWidget(l,14,1,1,1); + SB_AirMines = new QSpinBox(gbBasicSettings); + SB_AirMines->setRange(0, 200); + SB_AirMines->setValue(0); + SB_AirMines->setSingleStep(5); + glBSLayout->addWidget(SB_AirMines,14,2,1,1); + + l = new QLabel(gbBasicSettings); + l->setText(QLabel::tr("% Get Away Time")); + l->setWordWrap(true); + glBSLayout->addWidget(l,15,0,1,1); + l = new QLabel(gbBasicSettings); + l->setFixedSize(32,32); l->setPixmap(QPixmap(":/res/iconTime.png")); - glBSLayout->addWidget(l,14,1,1,1); + glBSLayout->addWidget(l,15,1,1,1); SB_GetAwayTime = new QSpinBox(gbBasicSettings); SB_GetAwayTime->setRange(0, 999); SB_GetAwayTime->setValue(100); SB_GetAwayTime->setSingleStep(25); - glBSLayout->addWidget(SB_GetAwayTime,14,2,1,1); + glBSLayout->addWidget(SB_GetAwayTime,15,2,1,1); l = new QLabel(gbBasicSettings); l->setText(QLabel::tr("World Edge")); l->setWordWrap(true); - glBSLayout->addWidget(l,15,0,1,1); + glBSLayout->addWidget(l,16,0,1,1); l = new QLabel(gbBasicSettings); l->setFixedSize(32,32); l->setPixmap(QPixmap(":/res/iconEarth.png")); - glBSLayout->addWidget(l,15,1,1,1); + glBSLayout->addWidget(l,16,1,1,1); CB_WorldEdge = new QComboBox(gbBasicSettings); CB_WorldEdge->insertItem(0, tr("None (Default)")); @@ -397,21 +411,21 @@ CB_WorldEdge->insertItem(2, tr("Bounce (Edges reflect)")); CB_WorldEdge->insertItem(3, tr("Sea (Edges connect to sea)")); /* CB_WorldEdge->insertItem(4, tr("Skybox")); */ - glBSLayout->addWidget(CB_WorldEdge,15,2,1,1); + glBSLayout->addWidget(CB_WorldEdge,16,2,1,1); l = new QLabel(gbBasicSettings); l->setText(QLabel::tr("Script parameter")); l->setWordWrap(true); - glBSLayout->addWidget(l,16,0,1,1); + glBSLayout->addWidget(l,17,0,1,1); l = new QLabel(gbBasicSettings); l->setFixedSize(32,32); l->setPixmap(QPixmap(":/res/iconBox.png")); - glBSLayout->addWidget(l,16,1,1,1); + glBSLayout->addWidget(l,17,1,1,1); LE_ScriptParam = new QLineEdit(gbBasicSettings); LE_ScriptParam->setMaxLength(240); - glBSLayout->addWidget(LE_ScriptParam,16,2,1,1); + glBSLayout->addWidget(LE_ScriptParam,17,2,1,1); l = new QLabel(gbBasicSettings); @@ -497,14 +511,15 @@ mapper->addMapping(SB_Mines, 32); mapper->addMapping(SB_MineDuds, 33); mapper->addMapping(SB_Explosives, 34); - mapper->addMapping(SB_HealthCrates, 35); - mapper->addMapping(SB_CrateHealth, 36); - mapper->addMapping(SB_WaterRise, 37); - mapper->addMapping(SB_HealthDecrease, 38); - mapper->addMapping(SB_RopeModifier, 39); - mapper->addMapping(SB_GetAwayTime, 40); - mapper->addMapping(CB_WorldEdge, 41, "currentIndex"); - mapper->addMapping(LE_ScriptParam, 42); + mapper->addMapping(SB_AirMines, 35); + mapper->addMapping(SB_HealthCrates, 36); + mapper->addMapping(SB_CrateHealth, 37); + mapper->addMapping(SB_WaterRise, 38); + mapper->addMapping(SB_HealthDecrease, 39); + mapper->addMapping(SB_RopeModifier, 40); + mapper->addMapping(SB_GetAwayTime, 41); + mapper->addMapping(CB_WorldEdge, 42, "currentIndex"); + mapper->addMapping(LE_ScriptParam, 43); mapper->toFirst(); } diff -r 1ff3dd3705b1 -r 99273b7afbff QTfrontend/ui/page/pagescheme.h --- a/QTfrontend/ui/page/pagescheme.h Mon Feb 16 22:33:15 2015 +0300 +++ b/QTfrontend/ui/page/pagescheme.h Thu Apr 02 21:09:56 2015 +0300 @@ -87,6 +87,7 @@ QSpinBox * SB_CrateHealth; QSpinBox * SB_MinesTime; QSpinBox * SB_Mines; + QSpinBox * SB_AirMines; QSpinBox * SB_MineDuds; QSpinBox * SB_Explosives; QSpinBox * SB_RopeModifier; diff -r 1ff3dd3705b1 -r 99273b7afbff QTfrontend/ui/widget/gamecfgwidget.cpp --- a/QTfrontend/ui/widget/gamecfgwidget.cpp Mon Feb 16 22:33:15 2015 +0300 +++ b/QTfrontend/ui/widget/gamecfgwidget.cpp Thu Apr 02 21:09:56 2015 +0300 @@ -316,18 +316,19 @@ bcfg << QString("e$minesnum %1").arg(schemeData(32).toInt()).toUtf8(); bcfg << QString("e$minedudpct %1").arg(schemeData(33).toInt()).toUtf8(); bcfg << QString("e$explosives %1").arg(schemeData(34).toInt()).toUtf8(); - bcfg << QString("e$healthprob %1").arg(schemeData(35).toInt()).toUtf8(); - bcfg << QString("e$hcaseamount %1").arg(schemeData(36).toInt()).toUtf8(); - bcfg << QString("e$waterrise %1").arg(schemeData(37).toInt()).toUtf8(); - bcfg << QString("e$healthdec %1").arg(schemeData(38).toInt()).toUtf8(); - bcfg << QString("e$ropepct %1").arg(schemeData(39).toInt()).toUtf8(); - bcfg << QString("e$getawaytime %1").arg(schemeData(40).toInt()).toUtf8(); - bcfg << QString("e$worldedge %1").arg(schemeData(41).toInt()).toUtf8(); + bcfg << QString("e$airmines %1").arg(schemeData(35).toInt()).toUtf8(); + bcfg << QString("e$healthprob %1").arg(schemeData(36).toInt()).toUtf8(); + bcfg << QString("e$hcaseamount %1").arg(schemeData(37).toInt()).toUtf8(); + bcfg << QString("e$waterrise %1").arg(schemeData(38).toInt()).toUtf8(); + bcfg << QString("e$healthdec %1").arg(schemeData(39).toInt()).toUtf8(); + bcfg << QString("e$ropepct %1").arg(schemeData(40).toInt()).toUtf8(); + bcfg << QString("e$getawaytime %1").arg(schemeData(41).toInt()).toUtf8(); + bcfg << QString("e$worldedge %1").arg(schemeData(42).toInt()).toUtf8(); bcfg << QString("e$template_filter %1").arg(pMapContainer->getTemplateFilter()).toUtf8(); bcfg << QString("e$feature_size %1").arg(pMapContainer->getFeatureSize()).toUtf8(); bcfg << QString("e$mapgen %1").arg(mapgen).toUtf8(); - if(!schemeData(42).isNull()) - bcfg << QString("e$scriptparam %1").arg(schemeData(42).toString()).toUtf8(); + if(!schemeData(43).isNull()) + bcfg << QString("e$scriptparam %1").arg(schemeData(43).toString()).toUtf8(); switch (mapgen) @@ -527,8 +528,8 @@ int num = GameSchemes->findText(pMapContainer->getCurrentScheme()); if (num != -1) GameSchemes->setCurrentIndex(num); - else - GameSchemes->setCurrentIndex(GameSchemes->findText("Default")); + //else + // GameSchemes->setCurrentIndex(GameSchemes->findText("Default")); } if (pMapContainer->getCurrentWeapons() == "locked") @@ -542,8 +543,8 @@ int num = WeaponsName->findText(pMapContainer->getCurrentWeapons()); if (num != -1) WeaponsName->setCurrentIndex(num); - else - WeaponsName->setCurrentIndex(WeaponsName->findText("Default")); + //else + // WeaponsName->setCurrentIndex(WeaponsName->findText("Default")); } if (pMapContainer->getCurrentScheme() != "locked" && pMapContainer->getCurrentWeapons() != "locked") @@ -586,7 +587,7 @@ if (sl.size() >= 42) { - sl[42].prepend('!'); + sl[sl.size()-1].prepend('!'); emit paramChanged("SCHEME", sl); // this is a stupid hack for the fact that SCHEME is being sent once, empty. Still need to find out why. } @@ -628,8 +629,8 @@ int num = GameSchemes->findText(scheme); if (num != -1) GameSchemes->setCurrentIndex(num); - else - GameSchemes->setCurrentIndex(GameSchemes->findText("Default")); + //else + // GameSchemes->setCurrentIndex(GameSchemes->findText("Default")); } if (weapons == "locked") @@ -643,8 +644,8 @@ int num = WeaponsName->findText(weapons); if (num != -1) WeaponsName->setCurrentIndex(num); - else - WeaponsName->setCurrentIndex(WeaponsName->findText("Default")); + //else + // WeaponsName->setCurrentIndex(WeaponsName->findText("Default")); } if (scheme != "locked" && weapons != "locked") diff -r 1ff3dd3705b1 -r 99273b7afbff gameServer/CoreTypes.hs --- a/gameServer/CoreTypes.hs Mon Feb 16 22:33:15 2015 +0300 +++ b/gameServer/CoreTypes.hs Thu Apr 02 21:09:56 2015 +0300 @@ -226,6 +226,7 @@ isRegisteredOnly :: Bool, isSpecial :: Bool, defaultHedgehogsNumber :: Int, + teamsNumberLimit :: Int, greeting :: B.ByteString, voting :: Maybe Voting, roomBansList :: ![B.ByteString], @@ -250,6 +251,7 @@ False False 4 + 8 "" Nothing [] diff -r 1ff3dd3705b1 -r 99273b7afbff gameServer/HWProtoCore.hs --- a/gameServer/HWProtoCore.hs Mon Feb 16 22:33:15 2015 +0300 +++ b/gameServer/HWProtoCore.hs Thu Apr 02 21:09:56 2015 +0300 @@ -71,11 +71,14 @@ h "WATCH" f = return [QueryReplay f] h "FIX" _ = handleCmd ["FIX"] h "UNFIX" _ = handleCmd ["UNFIX"] - h "GREETING" msg = handleCmd ["GREETING", msg] + h "GREETING" msg | not $ B.null msg = handleCmd ["GREETING", msg] h "CALLVOTE" msg | B.null msg = handleCmd ["CALLVOTE"] | otherwise = let (c, p) = extractParameters msg in if B.null p then handleCmd ["CALLVOTE", c] else handleCmd ["CALLVOTE", c, p] - h "VOTE" msg = handleCmd ["VOTE", upperCase msg] + h "VOTE" msg | not $ B.null msg = handleCmd ["VOTE", upperCase msg] + h "FORCE" msg | not $ B.null msg = handleCmd ["VOTE", upperCase msg, "FORCE"] + h "MAXTEAMS" n | not $ B.null n = handleCmd ["MAXTEAMS", n] + h "INFO" n | not $ B.null n = handleCmd ["INFO", n] h c p = return [Warning $ B.concat ["Unknown cmd: /", c, " ", p]] extractParameters p = let (a, b) = B.break (== ' ') p in (upperCase a, B.dropWhile (== ' ') b) diff -r 1ff3dd3705b1 -r 99273b7afbff gameServer/HWProtoInRoomState.hs --- a/gameServer/HWProtoInRoomState.hs Mon Feb 16 22:33:15 2015 +0300 +++ b/gameServer/HWProtoInRoomState.hs Thu Apr 02 21:09:56 2015 +0300 @@ -125,7 +125,7 @@ defaultHedgehogsNumber rm let newTeam = clNick `seq` TeamInfo clNick tName teamColor grave fort voicepack flag isRegistered dif hhNum (hhsList hhsInfo) return $ - if not . null . drop (maxTeams rm - 1) $ roomTeams then + if not . null . drop (teamsNumberLimit rm - 1) $ roomTeams then [Warning $ loc "too many teams"] else if canAddNumber roomTeams <= 0 then [Warning $ loc "too many hedgehogs"] @@ -389,6 +389,15 @@ s <- roomClientsChans return [AnswerClients s ["CHAT", n, B.unwords $ "/rnd" : rs], Random s rs] + +handleCmd_inRoom ["MAXTEAMS", n] = roomAdminOnly $ do + cl <- thisClient + let m = readInt_ n + if m < 2 || m > 8 then + return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/maxteams: specify number from 2 to 8"]] + else + return [ModifyRoom (\r -> r{teamsNumberLimit = m})] + handleCmd_inRoom ["FIX"] = serverAdminOnly $ return [ModifyRoom (\r -> r{isSpecial = True})] @@ -473,11 +482,11 @@ return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote hedgehogs: specify number from 1 to 8"]] -handleCmd_inRoom ["VOTE", m] = do +handleCmd_inRoom ("VOTE" : m : p) = do cl <- thisClient let b = if m == "YES" then Just True else if m == "NO" then Just False else Nothing if isJust b then - voted (fromJust b) + voted (p == ["FORCE"]) (fromJust b) else return [AnswerClients [sendChan cl] ["CHAT", "[server]", "vote: 'yes' or 'no'"]] diff -r 1ff3dd3705b1 -r 99273b7afbff gameServer/Votes.hs --- a/gameServer/Votes.hs Mon Feb 16 22:33:15 2015 +0300 +++ b/gameServer/Votes.hs Thu Apr 02 21:09:56 2015 +0300 @@ -26,6 +26,7 @@ import qualified Data.List as L import qualified Data.Map as Map import Data.Maybe +import Control.Applicative ------------------- import Utils import CoreTypes @@ -33,8 +34,8 @@ import EngineInteraction -voted :: Bool -> Reader (ClientIndex, IRnC) [Action] -voted vote = do +voted :: Bool -> Bool -> Reader (ClientIndex, IRnC) [Action] +voted forced vote = do cl <- thisClient rm <- thisRoom uid <- liftM clUID thisClient @@ -43,12 +44,15 @@ Nothing -> return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "There's no voting going on"]] Just voting -> - if uid `L.notElem` entitledToVote voting then + if (not forced) && (uid `L.notElem` entitledToVote voting) then return [] - else if uid `L.elem` map fst (votes voting) then + else if (not forced) && (uid `L.elem` map fst (votes voting)) then return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "You already have voted"]] + else if forced && (not $ isAdministrator cl) then + return [] else - actOnVoting $ voting{votes = (uid, vote):votes voting} + ((:) (AnswerClients [sendChan cl] ["CHAT", "[server]", loc "Your vote counted"])) + <$> (actOnVoting $ voting{votes = (uid, vote):votes voting}) where actOnVoting :: Voting -> Reader (ClientIndex, IRnC) [Action] @@ -57,9 +61,9 @@ let totalV = length $ entitledToVote vt let successV = totalV `div` 2 + 1 - if length contra > totalV - successV then + if (forced && not vote) || (length contra > totalV - successV) then closeVoting - else if length pro >= successV then do + else if (forced && vote) || (length pro >= successV) then do a <- act $ voteType vt c <- closeVoting return $ c ++ a @@ -117,10 +121,11 @@ let answers = concatMap (\t -> [ModifyRoom $ modifyTeam t{hhnum = h} , AnswerClients chans ["HH_NUM", teamname t, showB h]] - ) - $ + ) $ if length curteams * h > 48 then [] else curteams + ; + curteams = if isJust $ gameInfo rm then - teamsAtStart . fromJust . gameInfo $ rm + teamsAtStart . fromJust . gameInfo $ rm else teams rm diff -r 1ff3dd3705b1 -r 99273b7afbff gameServer/hedgewars-server.hs diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/SDLh.pas --- a/hedgewars/SDLh.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/SDLh.pas Thu Apr 02 21:09:56 2015 +0300 @@ -306,8 +306,12 @@ SDLK_BACKSPACE = 8; SDLK_RETURN = 13; SDLK_ESCAPE = 27; + SDLK_a = 97; + SDLK_c = 99; SDLK_q = 113; + SDLK_v = 118; SDLK_w = 119; + SDLK_x = 120; SDLK_DELETE = 127; SDLK_KP_ENTER = 271; SDLK_UP = 273; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/hwengine.pas --- a/hedgewars/hwengine.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/hwengine.pas Thu Apr 02 21:09:56 2015 +0300 @@ -162,7 +162,7 @@ if GameState = gsChat then begin // sdl on iphone supports only ashii keyboards and the unicode field is deprecated in sdl 1.3 - KeyPressChat(SDL_GetKeyFromScancode(event.key.keysym.sym), event.key.keysym.sym); //TODO correct for keymodifiers + KeyPressChat(SDL_GetKeyFromScancode(event.key.keysym.sym), event.key.keysym.sym, event.key.keysym.modifier); end else if GameState >= gsGame then ProcessKey(event.key); @@ -208,7 +208,7 @@ {$ELSE} SDL_KEYDOWN: if GameState = gsChat then - KeyPressChat(event.key.keysym.unicode, event.key.keysym.sym) + KeyPressChat(event.key.keysym.unicode, event.key.keysym.sym, event.key.keysym.modifier) else if GameState >= gsGame then ProcessKey(event.key); SDL_KEYUP: diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/pas2cRedo.pas --- a/hedgewars/pas2cRedo.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/pas2cRedo.pas Thu Apr 02 21:09:56 2015 +0300 @@ -66,12 +66,12 @@ Now : function : integer; - new, dispose, FillChar, Move : procedure; + new, dispose, FillChar, Insert, Delete, Move : procedure; trunc, round : function : integer; abs, sqr : function : integer; - StrPas, FormatDateTime, copy, delete, str, PosS, trim, LowerCase : function : shortstring; + StrPas, FormatDateTime, copy, str, PosS, trim, LowerCase : function : shortstring; pos : function : integer; StrToInt : function : integer; SetLength, SetLengthA, val, StrDispose, StrCopy : procedure; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uAmmos.pas --- a/hedgewars/uAmmos.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uAmmos.pas Thu Apr 02 21:09:56 2015 +0300 @@ -400,12 +400,12 @@ AddCaption(s, Team^.Clan^.Color, capgrpAmmoinfo); if (Propz and ammoprop_NeedTarget) <> 0 then begin - if Gear <> nil then Gear^.State:= Gear^.State or gstHHChooseTarget; + if Gear <> nil then Gear^.State:= Gear^.State or gstChooseTarget; isCursorVisible:= true end else begin - if Gear <> nil then Gear^.State:= Gear^.State and (not gstHHChooseTarget); + if Gear <> nil then Gear^.State:= Gear^.State and (not gstChooseTarget); isCursorVisible:= false end; end diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uChat.pas --- a/hedgewars/uChat.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uChat.pas Thu Apr 02 21:09:56 2015 +0300 @@ -28,13 +28,15 @@ procedure CleanupInput; procedure AddChatString(s: shortstring); procedure DrawChat; -procedure KeyPressChat(Key, Sym: Longword); +procedure KeyPressChat(Key, Sym: Longword; Modifier: Word); procedure SendHogSpeech(s: shortstring); +procedure CopyToClipboard(var newContent: shortstring); implementation uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO, uScript, uRenderUtils; const MaxStrIndex = 27; + MaxInputStrLen = 240; type TChatLine = record Tex: PTexture; @@ -45,22 +47,33 @@ end; TChatCmd = (ccQuit, ccPause, ccFinish, ccShowHistory, ccFullScreen); +type TInputStrL = array[0..260] of byte; + var Strs: array[0 .. MaxStrIndex] of TChatLine; MStrs: array[0 .. MaxStrIndex] of shortstring; LocalStrs: array[0 .. MaxStrIndex] of shortstring; + LocalStrsL: array[0 .. MaxStrIndex] of TInputStrL; missedCount: LongWord; lastStr: LongWord; localLastStr: LongInt; history: LongInt; visibleCount: LongWord; InputStr: TChatLine; - InputStrL: array[0..260] of char; // for full str + 4-byte utf-8 char + InputStrL: TInputStrL; // for full str + 4-byte utf-8 char ChatReady: boolean; showAll: boolean; liveLua: boolean; ChatHidden: boolean; + firstDraw: boolean; + InputLinePrefix: TChatLine; + // cursor + cursorPos, cursorX, selectedPos, selectionDx: LongInt; + LastKeyPressTick: LongWord; + const + InputStrLNoPred: byte = 255; + colors: array[#0..#6] of TSDL_Color = ( (r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything (r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White] @@ -85,6 +98,59 @@ const Padding = 2; ClHeight = 2 * Padding + 16; // font height +function charIsForHogSpeech(c: char): boolean; +begin +exit((c = '"') or (c = '''') or (c = '-')); +end; + +procedure ResetSelection(); +begin + selectedPos:= -1; +end; + +procedure UpdateCursorCoords(); +var font: THWFont; + str : shortstring; + coff, soff: LongInt; +begin + if cursorPos = selectedPos then + ResetSelection(); + + // calculate cursor offset + + str:= InputStr.s; + font:= CheckCJKFont(ansistring(str), fnt16); + + // get only substring before cursor to determine length + // SetLength(str, cursorPos); // makes pas2c unhappy + str[0]:= char(cursorPos); + // get render size of text + TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @coff, nil); + + cursorX:= 2 + coff; + + // calculate selection width on screen + if selectedPos >= 0 then + begin + if selectedPos > cursorPos then + str:= InputStr.s; + // SetLength(str, selectedPos); // makes pas2c unhappy + str[0]:= char(selectedPos); + TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @soff, nil); + selectionDx:= soff - coff; + end + else + selectionDx:= 0; +end; + + +procedure ResetCursor(); +begin + ResetSelection(); + cursorPos:= 0; + UpdateCursorCoords(); +end; + procedure RenderChatLineTex(var cl: TChatLine; var str: shortstring); var strSurface, resSurface: PSDL_Surface; @@ -139,12 +205,19 @@ begin cl.s:= str; color:= colors[#6]; - str:= UserNick + '> ' + str + '_' + str:= str + ' '; end else begin - color:= colors[str[1]]; - delete(str, 1, 1); + if str[1] <= High(colors) then + begin + color:= colors[str[1]]; + delete(str, 1, 1); + end + // fallback if invalid color + else + color:= colors[Low(colors)]; + cl.s:= str; end; @@ -190,8 +263,28 @@ inc(visibleCount) end; +procedure CheckPasteBuffer(); forward; + +procedure UpdateInputLinePrefix(); +begin +if liveLua then + begin + InputLinePrefix.color:= colors[#1]; + InputLinePrefix.s:= '[Lua] >'; + end +else + begin + InputLinePrefix.color:= colors[#6]; + InputLinePrefix.s:= UserNick + '>'; + end; + +FreeAndNilTexture(InputLinePrefix.Tex); +end; + procedure DrawChat; var i, t, left, top, cnt: LongInt; + selRect: TSDL_Rect; + c: char; begin ChatReady:= true; // maybe move to somewhere else? @@ -204,8 +297,78 @@ // draw chat input line first and under all other lines if (GameState = gsChat) and (InputStr.Tex <> nil) then + begin + CheckPasteBuffer(); + + if InputLinePrefix.Tex = nil then + RenderChatLineTex(InputLinePrefix, InputLinePrefix.s); + + DrawTexture(left, top, InputLinePrefix.Tex); + inc(left, InputLinePrefix.Width); DrawTexture(left, top, InputStr.Tex); + if firstDraw then + begin + UpdateCursorCoords(); + firstDraw:= false; + end; + + if selectedPos < 0 then + begin + // draw cursor + if ((RealTicks - LastKeyPressTick) and 512) < 256 then + DrawLineOnScreen(left + cursorX, top + 2, left + cursorX, top + ClHeight - 2, 2.0, $00, $FF, $FF, $FF); + end + else // draw selection + begin + selRect.y:= top + 2; + selRect.h:= clHeight - 4; + if selectionDx < 0 then + begin + selRect.x:= left + cursorX + selectionDx; + selRect.w:= -selectionDx; + end + else + begin + selRect.x:= left + cursorX; + selRect.w:= selectionDx; + end; + + DrawRect(selRect, $FF, $FF, $FF, $40, true); + end; + + dec(left, InputLinePrefix.Width); + + + if (Length(InputStr.s) > 0) and ((CursorPos = 1) or (CursorPos = 2)) then + begin + c:= InputStr.s[1]; + if charIsForHogSpeech(c) then + begin + SpeechHogNumber:= 0; + if Length(InputStr.s) > 1 then + begin + c:= InputStr.s[2]; + if (c > '0') and (c < '9') then + SpeechHogNumber:= byte(c) - 48; + end; + // default to current hedgehog (if own) or first hedgehog + if SpeechHogNumber = 0 then + begin + if not CurrentTeam^.ExtDriven then + SpeechHogNumber:= CurrentTeam^.CurrHedgehog + 1 + else + SpeechHogNumber:= 1; + end; + end; + end + else + SpeechHogNumber:= -1; + end +else + SpeechHogNumber:= -1; + +// draw chat lines if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then begin if MissedCount <> 0 then // there are chat strings we missed, so print them now @@ -264,6 +427,7 @@ // put in input history localLastStr:= (localLastStr + 1) mod MaxStrIndex; LocalStrs[localLastStr]:= s; + LocalStrsL[localLastStr]:= InputStrL; end; t:= LocalTeam; @@ -365,6 +529,7 @@ AddFileLog('[Lua] chat input string parsing disabled'); AddChatString(#3 + 'Lua parsing: OFF'); end; + UpdateInputLinePrefix(); end; exit end; @@ -410,26 +575,308 @@ ResetKbd; end; -procedure KeyPressChat(Key, Sym: Longword); +procedure DelBytesFromInputStrBack(endIdx: integer; count: byte); +var i, startIdx: integer; +begin + // nothing to do if count is 0 + if count = 0 then + exit; + + // first byte to delete + startIdx:= endIdx - (count - 1); + + // delete bytes from string + Delete(InputStr.s, startIdx, count); + + // wipe utf8 info for deleted char + InputStrL[endIdx]:= InputStrLNoPred; + + // shift utf8 char info to reflect new string + for i:= endIdx + 1 to Length(InputStr.s) + count do + begin + if InputStrL[i] <> InputStrLNoPred then + begin + InputStrL[i-count]:= InputStrL[i] - count; + InputStrL[i]:= InputStrLNoPred; + end; + end; + + SetLine(InputStr, InputStr.s, true); +end; + +// returns count of removed bytes +function DelCharFromInputStr(idx: integer): integer; +var btw: byte; +begin + // note: idx is always at last byte of utf8 chars. cuz relevant for InputStrL + + if (Length(InputStr.s) < 1) or (idx < 1) or (idx > Length(InputStr.s)) then + exit(0); + + btw:= byte(idx) - InputStrL[idx]; + + DelCharFromInputStr:= btw; + + DelBytesFromInputStrBack(idx, btw); +end; + +// unchecked +procedure DoCursorStepForward(); +begin + // go to end of next utf8-char + repeat + inc(cursorPos); + until InputStrL[cursorPos] <> InputStrLNoPred; +end; + +procedure DeleteSelected(); +begin + if (selectedPos >= 0) and (cursorPos <> selectedPos) then + begin + DelBytesFromInputStrBack(max(cursorPos, selectedPos), abs(selectedPos-cursorPos)); + cursorPos:= min(cursorPos, selectedPos); + ResetSelection(); + end; + UpdateCursorCoords(); +end; + +procedure HandleSelection(enabled: boolean); +begin +if enabled then + begin + if selectedPos < 0 then + selectedPos:= cursorPos; + end +else + ResetSelection(); +end; + +type TCharSkip = ( none, wspace, numalpha, special ); + +function GetInputCharSkipClass(index: LongInt): TCharSkip; +var c: char; +begin + // multi-byte chars counts as letter + if (index > 1) and (InputStrL[index] <> index - 1) then + exit(numalpha); + + c:= InputStr.s[index]; + + // non-ascii counts as letter + if c > #127 then + exit(numalpha); + + // low-ascii whitespaces and DEL + if (c < #33) or (c = #127) then + exit(wspace); + + // low-ascii special chars + if c < #48 then + exit(special); + + // digits + if c < #58 then + exit(numalpha); + + // make c upper-case + if c > #96 then + c:= char(byte(c) - 32); + + // letters + if (c > #64) and (c < #90) then + exit(numalpha); + + // remaining ascii are special chars + exit(special); +end; + +// skip from word to word, similar to Qt +procedure SkipInputChars(skip: TCharSkip; backwards: boolean); +begin +if backwards then + begin + // skip trailing whitespace, similar to Qt + while (skip = wspace) and (cursorPos > 0) do + begin + skip:= GetInputCharSkipClass(cursorPos); + if skip = wspace then + cursorPos:= InputStrL[cursorPos]; + end; + // skip same-type chars + while (cursorPos > 0) and (GetInputCharSkipClass(cursorPos) = skip) do + cursorPos:= InputStrL[cursorPos]; + end +else + begin + // skip same-type chars + while cursorPos < Length(InputStr.s) do + begin + DoCursorStepForward(); + if (GetInputCharSkipClass(cursorPos) <> skip) then + begin + // go back 1 char + cursorPos:= InputStrL[cursorPos]; + break; + end; + end; + // skip trailing whitespace, similar to Qt + while cursorPos < Length(InputStr.s) do + begin + DoCursorStepForward(); + if (GetInputCharSkipClass(cursorPos) <> wspace) then + begin + // go back 1 char + cursorPos:= InputStrL[cursorPos]; + break; + end; + end; + end; +end; + +procedure CopyToClipboard(var newContent: shortstring); +begin + SendIPC(_S'y' + copy(newContent, 1, 253) + #0); +end; + +procedure CopySelectionToClipboard(); +var selection: shortstring; +begin + if selectedPos >= 0 then + begin + selection:= copy(InputStr.s, min(CursorPos, selectedPos) + 1, abs(CursorPos - selectedPos)); + CopyToClipboard(selection); + end; +end; + +// TODO: honor utf8, don't break utf8 chars when shifting chars beyond limit +procedure InsertIntoInputStr(s: shortstring); +var i, l, il, lastc: integer; +begin + // safe length for string + l:= min(MaxInputStrLen-cursorPos, Length(s)); + s:= copy(s,1,l); + + // if we insert rather than append, shift info in InputStrL accordingly + if cursorPos < Length(InputStr.s) then + begin + for i:= Length(InputStr.s) downto cursorPos + 1 do + begin + if InputStrL[i] <> InputStrLNoPred then + begin + il:= i + l; + // only shift if not overflowing + if il <= MaxInputStrLen then + InputStrL[il]:= InputStrL[i] + l; + InputStrL[i]:= InputStrLNoPred; + end; + end; + end; + + InputStrL[cursorPos + l]:= cursorPos; + // insert string truncated to safe length + Insert(s, InputStr.s, cursorPos + 1); + if Length(InputStr.s) > MaxInputStrLen then + InputStr.s[0]:= char(MaxInputStrLen); + + SetLine(InputStr, InputStr.s, true); + + // move cursor to end of inserted string + lastc:= MaxInputStrLen; + cursorPos:= min(lastc, cursorPos + l); + UpdateCursorCoords(); +end; + +procedure PasteFromClipboard(); +begin + SendIPC(_S'Y'); +end; + +procedure CheckPasteBuffer(); +begin + if Length(ChatPasteBuffer) > 0 then + begin + InsertIntoInputStr(ChatPasteBuffer); + ChatPasteBuffer:= ''; + end; +end; + +procedure KeyPressChat(Key, Sym: Longword; Modifier: Word); const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0); var i, btw, index: integer; utf8: shortstring; - action: boolean; + action, selMode, ctrl: boolean; + skip: TCharSkip; begin + LastKeyPressTick:= RealTicks; action:= true; + + CheckPasteBuffer(); + + selMode:= (modifier and (KMOD_LSHIFT or KMOD_RSHIFT)) <> 0; + ctrl:= (modifier and (KMOD_LCTRL or KMOD_RCTRL)) <> 0; + skip:= none; + case Sym of SDLK_BACKSPACE: begin - if Length(InputStr.s) > 0 then + if selectedPos < 0 then begin - InputStr.s[0]:= InputStrL[byte(InputStr.s[0])]; - SetLine(InputStr, InputStr.s, true) + if ctrl then + skip:= GetInputCharSkipClass(cursorPos); + + // remove char before cursor + dec(cursorPos, DelCharFromInputStr(cursorPos)); + + // delete more if ctrl is held + if ctrl and (selectedPos < 0) then + begin + HandleSelection(true); + SkipInputChars(skip, true); + DeleteSelected(); + end + else + UpdateCursorCoords(); + end + else + DeleteSelected(); + end; + SDLK_DELETE: + begin + if selectedPos < 0 then + begin + // remove char after cursor + if cursorPos < Length(InputStr.s) then + begin + DoCursorStepForward(); + if ctrl then + skip:= GetInputCharSkipClass(cursorPos); + + // delete char + dec(cursorPos, DelCharFromInputStr(cursorPos)); + + // delete more if ctrl is held + if ctrl and (cursorPos < Length(InputStr.s)) then + begin + HandleSelection(true); + SkipInputChars(skip, false); + DeleteSelected(); + end; + end + else + UpdateCursorCoords(); + end + else + DeleteSelected(); end; SDLK_ESCAPE: begin if Length(InputStr.s) > 0 then - SetLine(InputStr, '', true) + begin + SetLine(InputStr, '', true); + FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred); + ResetCursor(); + end else CleanupInput end; SDLK_RETURN, SDLK_KP_ENTER: @@ -437,7 +884,9 @@ if Length(InputStr.s) > 0 then begin AcceptChatString(InputStr.s); - SetLine(InputStr, '', false) + SetLine(InputStr, '', false); + FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred); + ResetCursor(); end; CleanupInput end; @@ -447,20 +896,150 @@ if (Sym = SDLK_DOWN) and (history > 0) then dec(history); index:= localLastStr - history + 1; if (index > localLastStr) then - SetLine(InputStr, '', true) - else SetLine(InputStr, LocalStrs[index], true) + begin + SetLine(InputStr, '', true); + FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred); + end + else + begin + SetLine(InputStr, LocalStrs[index], true); + InputStrL:= LocalStrsL[index]; + end; + cursorPos:= Length(InputStr.s); + ResetSelection(); + UpdateCursorCoords(); + end; + SDLK_HOME: + begin + if cursorPos > 0 then + begin + HandleSelection(selMode); + cursorPos:= 0; + end + else if (not selMode) then + ResetSelection(); + + UpdateCursorCoords(); + end; + SDLK_END: + begin + i:= Length(InputStr.s); + if cursorPos < i then + begin + HandleSelection(selMode); + cursorPos:= i; + end + else if (not selMode) then + ResetSelection(); + + UpdateCursorCoords(); end; - SDLK_RIGHT, SDLK_LEFT, SDLK_DELETE, - SDLK_HOME, SDLK_END, + SDLK_LEFT: + begin + if cursorPos > 0 then + begin + + if ctrl then + skip:= GetInputCharSkipClass(cursorPos); + + if selMode or (selectedPos < 0) then + begin + HandleSelection(selMode); + // go to end of previous utf8-char + cursorPos:= InputStrL[cursorPos]; + end + else // if we're leaving selection mode, jump to its left end + begin + cursorPos:= min(cursorPos, selectedPos); + ResetSelection(); + end; + + if ctrl then + SkipInputChars(skip, true); + + end + else if (not selMode) then + ResetSelection(); + + UpdateCursorCoords(); + end; + SDLK_RIGHT: + begin + if cursorPos < Length(InputStr.s) then + begin + + if selMode or (selectedPos < 0) then + begin + HandleSelection(selMode); + DoCursorStepForward(); + end + else // if we're leaving selection mode, jump to its right end + begin + cursorPos:= max(cursorPos, selectedPos); + ResetSelection(); + end; + + if ctrl then + SkipInputChars(GetInputCharSkipClass(cursorPos), false); + + end + else if (not selMode) then + ResetSelection(); + + UpdateCursorCoords(); + end; SDLK_PAGEUP, SDLK_PAGEDOWN: begin // ignore me!!! end; + SDLK_a: + begin + // select all + if ctrl then + begin + ResetSelection(); + cursorPos:= 0; + HandleSelection(true); + cursorPos:= Length(InputStr.s); + UpdateCursorCoords(); + end + else + action:= false; + end; + SDLK_c: + begin + // copy + if ctrl then + CopySelectionToClipboard() + else + action:= false; + end; + SDLK_v: + begin + // paste + if ctrl then + PasteFromClipboard() + else + action:= false; + end; + SDLK_x: + begin + // cut + if ctrl then + begin + CopySelectionToClipboard(); + DeleteSelected(); + end + else + action:= false; + end; else action:= false; end; if not action and (Key <> 0) then begin + DeleteSelected(); + if (Key < $80) then btw:= 1 else if (Key < $800) then @@ -480,11 +1059,18 @@ utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8; - if byte(InputStr.s[0]) + btw > 240 then + if Length(InputStr.s) + btw > MaxInputStrLen then exit; - InputStrL[byte(InputStr.s[0]) + btw]:= InputStr.s[0]; - SetLine(InputStr, InputStr.s + utf8, true) + if (Length(InputStr.s) = 0) and (Length(utf8) = 1) and (charIsForHogSpeech(utf8[1])) then + begin + InsertIntoInputStr(utf8); + InsertIntoInputStr(utf8); + cursorPos:= 1; + UpdateCursorCoords(); + end + else + InsertIntoInputStr(utf8); end end; @@ -539,7 +1125,14 @@ if length(s) = 0 then SetLine(InputStr, '', true) else - SetLine(InputStr, '/team ', true) + begin + SetLine(InputStr, '/team ', true); + // update InputStrL and cursor accordingly + // this allows cursor-jumping over '/team ' as if it was a single char + InputStrL[6]:= 0; + cursorPos:= 6; + UpdateCursorCoords(); + end; end; procedure initModule; @@ -560,15 +1153,25 @@ missedCount:= 0; liveLua:= false; ChatHidden:= false; + firstDraw:= true; + InputLinePrefix.Tex:= nil; + UpdateInputLinePrefix(); + inputStr.s:= ''; inputStr.Tex := nil; for i:= 0 to MaxStrIndex do Strs[i].Tex := nil; + + FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred); + + LastKeyPressTick:= 0; + ResetCursor(); end; procedure freeModule; var i: ShortInt; begin + FreeAndNilTexture(InputLinePrefix.Tex); FreeAndNilTexture(InputStr.Tex); for i:= 0 to MaxStrIndex do FreeAndNilTexture(Strs[i].Tex); diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uCollisions.pas --- a/hedgewars/uCollisions.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uCollisions.pas Thu Apr 02 21:09:56 2015 +0300 @@ -52,7 +52,7 @@ function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word; inline; function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; -function TestRectancleForObstacle(x1, y1, x2, y2: LongInt; landOnly: boolean): boolean; +function TestRectangleForObstacle(x1, y1, x2, y2: LongInt; landOnly: boolean): boolean; function CheckCoordInWater(X, Y: LongInt): boolean; inline; @@ -398,11 +398,11 @@ Gear^.Y:= Gear^.Y - int2hwFloat(ShiftY) end; -function TestRectancleForObstacle(x1, y1, x2, y2: LongInt; landOnly: boolean): boolean; +function TestRectangleForObstacle(x1, y1, x2, y2: LongInt; landOnly: boolean): boolean; var x, y: LongInt; TestWord: LongWord; begin -TestRectancleForObstacle:= true; +TestRectangleForObstacle:= true; if landOnly then TestWord:= 255 @@ -431,7 +431,7 @@ if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > TestWord) then exit; -TestRectancleForObstacle:= false +TestRectangleForObstacle:= false end; function CalcSlopeTangent(Gear: PGear; collisionX, collisionY: LongInt; var outDeltaX, outDeltaY: LongInt; TestWord: LongWord): boolean; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uCommandHandlers.pas --- a/hedgewars/uCommandHandlers.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uCommandHandlers.pas Thu Apr 02 21:09:56 2015 +0300 @@ -27,6 +27,7 @@ implementation uses uCommands, uTypes, uVariables, uIO, uDebug, uConsts, uScript, uUtils, SDLh, uWorld, uRandom, uCaptions + , uVisualGearsList {$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF}; var prevGState: TGameState = gsConfirm; @@ -748,6 +749,11 @@ cLandMines:= StrToInt(s) end; +procedure chAirMines(var s: shortstring); +begin +cAirMines:= StrToInt(s) +end; + procedure chExplosives(var s: shortstring); begin cExplosives:= StrToInt(s) @@ -771,7 +777,14 @@ procedure chFastUntilLag(var s: shortstring); begin -fastUntilLag:= StrToInt(s) <> 0 + fastUntilLag:= StrToInt(s) <> 0; + + if not fastUntilLag then + begin + // update health bars and the wind indicator + AddVisualGear(0, 0, vgtTeamHealthSorter); + AddVisualGear(0, 0, vgtSmoothWindBar) + end end; procedure chCampVar(var s:shortstring); @@ -837,6 +850,7 @@ RegisterVariable('getawaytime' , @chGetAwayTime , false); RegisterVariable('minedudpct',@chMineDudPercent, false); RegisterVariable('minesnum', @chLandMines , false); + RegisterVariable('airmines', @chAirMines , false); RegisterVariable('explosives',@chExplosives , false); RegisterVariable('gmflags' , @chGameFlags , false); RegisterVariable('turntime', @chHedgehogTurnTime, false); diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uConsts.pas --- a/hedgewars/uConsts.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uConsts.pas Thu Apr 02 21:09:56 2015 +0300 @@ -215,7 +215,7 @@ gstAttacked = $00000008; gstAttacking = $00000010; gstCollision = $00000020; - gstHHChooseTarget = $00000040; + gstChooseTarget = $00000040; gstHHJumping = $00000100; gsttmpFlag = $00000200; gstHHThinking = $00000800; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uGame.pas --- a/hedgewars/uGame.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uGame.pas Thu Apr 02 21:09:56 2015 +0300 @@ -102,8 +102,9 @@ if isInLag then case GameType of gmtNet: begin - // just update the health bars + // update health bars and the wind indicator AddVisualGear(0, 0, vgtTeamHealthSorter); + AddVisualGear(0, 0, vgtSmoothWindBar); break; end; gmtDemo, gmtRecord: begin diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uGears.pas --- a/hedgewars/uGears.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uGears.pas Thu Apr 02 21:09:56 2015 +0300 @@ -33,7 +33,7 @@ * effects are called "Visual Gears" and defined in the respective unit! *) interface -uses uConsts, uFloat, uTypes, uChat; +uses uConsts, uFloat, uTypes, uChat, uCollisions; procedure initModule; procedure freeModule; @@ -394,7 +394,7 @@ if (CurrentHedgehog^.Gear^.State and gstAttacked <> 0) and (Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then begin - CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstHHChooseTarget; + CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstChooseTarget; isCursorVisible := true end; CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State and (not gstAttacked); @@ -551,6 +551,9 @@ end; Gear:= Gear^.NextGear end; + +if SpeechHogNumber > 0 then + DrawHHOrder(); end; procedure FreeGearsList; @@ -567,7 +570,7 @@ end; procedure AddMiscGears; -var p,i,j, unplaced: Longword; +var p,i,j,t,h,unplaced: Longword; rx, ry: LongInt; rdx, rdy: hwFloat; Gear: PGear; @@ -604,6 +607,70 @@ inc(i) end; +i:= 0; +j:= 0; +p:= 0; // 0 searching, 1 bad position, 2 added. +unplaced:= 0; +if cAirMines > 0 then + Gear:= AddGear(0, 0, gtAirMine, 0, _0, _0, 0); +while (i < cAirMines) and (j < 1000*cAirMines) do + begin + p:= 0; + if hasBorder then + begin + rx:= leftX+GetRandom(rightX-leftX-16)+8; + ry:= topY+GetRandom(LAND_HEIGHT-topY-16)+8 + end + else + begin + rx:= leftX+GetRandom(rightX-leftX+400)-200; + ry:= topY+GetRandom(LAND_HEIGHT-topY+400)-200 + end; + Gear^.X:= int2hwFloat(rx); + Gear^.Y:= int2hwFloat(ry); + if CheckLandValue(rx, ry, $FFFF) and + (TestCollisionYwithGear(Gear,-1) = 0) and + (TestCollisionXwithGear(Gear, 1) = 0) and + (TestCollisionXwithGear(Gear,-1) = 0) and + (TestCollisionYwithGear(Gear, 1) = 0) then + begin + t:= 0; + while (t < TeamsCount) and (p = 0) do + begin + h:= 0; + with TeamsArray[t]^ do + while (h < cMaxHHIndex) and (p = 0) do + begin + if (Hedgehogs[h].Gear <> nil) then + begin + rdx:=Gear^.X-Hedgehogs[h].Gear^.X; + rdy:=Gear^.Y-Hedgehogs[h].Gear^.Y; + if (Gear^.Angle < $FFFFFFFF) and + ((rdx.Round+rdy.Round < Gear^.Angle) and + (hwRound(hwSqr(rdx) + hwSqr(rdy)) < sqr(Gear^.Angle))) then + begin +// Debug line. Remove later +// AddFileLog('Too Close to Hog @ (' + inttostr(rx) + ',' + inttostr(ry) + ')'); + + p:= 1 + end + end; + inc(h) + end; + inc(t) + end; + if p = 0 then + begin + inc(i); + AddFileLog('Placed Air Mine @ (' + inttostr(rx) + ',' + inttostr(ry) + ')'); + if i < cAirMines then + Gear:= AddGear(0, 0, gtAirMine, 0, _0, _0, 0) + end + end; + inc(j) + end; +if p <> 0 then DeleteGear(Gear); + if (GameFlags and gfLowGravity) <> 0 then begin cGravity:= cMaxWindSpeed; @@ -930,6 +997,7 @@ @doStepHedgehog, @doStepMine, @doStepCase, + @doStepAirMine, @doStepCase, @doStepBomb, @doStepShell, @@ -991,8 +1059,7 @@ @doStepIceGun, @doStepAddAmmo, @doStepGenericFaller, - @doStepKnife, - @doStepAirMine); + @doStepKnife); begin doStepHandlers:= handlers; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uGearsHandlersMess.pas --- a/hedgewars/uGearsHandlersMess.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uGearsHandlersMess.pas Thu Apr 02 21:09:56 2015 +0300 @@ -1382,6 +1382,7 @@ dec(TurnTimeLeft) else begin + HHGear^.State := HHGear^.State and (not gstNotKickable); DeleteGear(Gear); AfterAttack end; @@ -1762,11 +1763,16 @@ var i,t,targDist,tmpDist: LongWord; targ, tmpG: PGear; trackSpeed, airFriction, tX, tY: hwFloat; + isUnderwater: Boolean; begin + isUnderwater:= CheckCoordInWater(hwRound(Gear^.X), hwRound(Gear^.Y) + Gear^.Radius); if Gear^.Pos > 0 then begin airFriction:= _1; - dec(airFriction.QWordValue,Gear^.Pos); + if isUnderwater then + dec(airFriction.QWordValue,Gear^.Pos*2) + else + dec(airFriction.QWordValue,Gear^.Pos); Gear^.dX:= Gear^.dX*airFriction; Gear^.dY:= Gear^.dY*airFriction end; @@ -1799,14 +1805,14 @@ ((TurnTimeLeft < cHedgehogTurnTime) and (cHedgehogTurnTime-TurnTimeLeft < 5000)) or (Gear^.State and gsttmpFlag = 0) or (Gear^.Angle = 0) then - gear^.State:= gear^.State and (not gstHHChooseTarget) + gear^.State:= gear^.State and (not gstChooseTarget) else if // todo, allow not finding new target, set timeout on target retention (Gear^.State and gstAttacking = 0) and ((GameTicks and $FF) = 17) and (GameTicks > Gear^.FlightTime) then // recheck hunted hog begin - gear^.State:= gear^.State or gstHHChooseTarget; + gear^.State:= gear^.State or gstChooseTarget; if targ <> nil then targDist:= Distance(Gear^.X-targ^.X,Gear^.Y-targ^.Y).Round else targDist:= 0; @@ -1837,9 +1843,12 @@ if targ <> nil then begin trackSpeed:= _0; - trackSpeed.QWordValue:= Gear^.Power; + if isUnderwater then + trackSpeed.QWordValue:= Gear^.Power div 2 + else + trackSpeed.QWordValue:= Gear^.Power; if (Gear^.X < targ^.X) and (Gear^.dX < _0_1) then - Gear^.dX:= Gear^.dX+trackSpeed + Gear^.dX:= Gear^.dX+trackSpeed // please leave as an add. I like the effect else if (Gear^.X > targ^.X) and (Gear^.dX > -_0_1) then Gear^.dX:= Gear^.dX-trackSpeed; if (Gear^.Y < targ^.Y) and (Gear^.dY < _0_1) then @@ -2747,7 +2756,7 @@ end; HHGear^.Message := HHGear^.Message and (not gmAttack); HHGear^.State := HHGear^.State and (not gstAttacking); - HHGear^.State := HHGear^.State or gstHHChooseTarget; + HHGear^.State := HHGear^.State or gstChooseTarget; isCursorVisible := true; DeleteGear(Gear) end @@ -2832,7 +2841,7 @@ begin HHGear^.Message := HHGear^.Message and (not gmAttack); HHGear^.State := HHGear^.State and (not gstAttacking); - HHGear^.State := HHGear^.State or gstHHChooseTarget; + HHGear^.State := HHGear^.State or gstChooseTarget; isCursorVisible := true; warn:= AddVisualGear(Gear^.Target.X, oy, vgtNoPlaceWarn, 0, true); if warn <> nil then @@ -3131,13 +3140,6 @@ //////////////////////////////////////////////////////////////////////////////// -const cakeh = 27; -var - CakePoints: array[0..Pred(cakeh)] of record - x, y: hwFloat; - end; - CakeI: Longword; - procedure doStepCakeExpl(Gear: PGear); begin AllInactive := false; @@ -3204,6 +3206,7 @@ procedure doStepCakeWork(Gear: PGear); var tdx, tdy: hwFloat; + cakeData: PCakeData; begin AllInactive := false; @@ -3222,24 +3225,31 @@ Gear^.RenderTimer := false; Gear^.doStep := @doStepCakeDown; exit - end; + end + else if Gear^.Timer < 6000 then + Gear^.RenderTimer:= true; if not cakeStep(Gear) then Gear^.doStep:= @doStepCakeFall; if Gear^.Tag = 0 then begin - CakeI := (CakeI + 1) mod cakeh; - tdx := CakePoints[CakeI].x - Gear^.X; - tdy := - CakePoints[CakeI].y + Gear^.Y; - CakePoints[CakeI].x := Gear^.X; - CakePoints[CakeI].y := Gear^.Y; - Gear^.DirAngle := DxDy2Angle(tdx, tdy); + cakeData:= PCakeData(Gear^.Data); + with cakeData^ do + begin + CakeI := (CakeI + 1) mod cakeh; + tdx := CakePoints[CakeI].x - Gear^.X; + tdy := - CakePoints[CakeI].y + Gear^.Y; + CakePoints[CakeI].x := Gear^.X; + CakePoints[CakeI].y := Gear^.Y; + Gear^.DirAngle := DxDy2Angle(tdx, tdy); + end; end; end; procedure doStepCakeUp(Gear: PGear); var i: Longword; + cakeData: PCakeData; begin AllInactive := false; @@ -3250,12 +3260,16 @@ if Gear^.Pos = 6 then begin - for i:= 0 to Pred(cakeh) do + cakeData:= PCakeData(Gear^.Data); + with cakeData^ do begin - CakePoints[i].x := Gear^.X; - CakePoints[i].y := Gear^.Y + for i:= 0 to Pred(cakeh) do + begin + CakePoints[i].x := Gear^.X; + CakePoints[i].y := Gear^.Y + end; + CakeI := 0; end; - CakeI := 0; Gear^.doStep := @doStepCakeWork end else @@ -3559,6 +3573,11 @@ AllInactive := false; dec(Gear^.Timer); HHGear := Gear^.Hedgehog^.Gear; + if HHGear = nil then + begin + DeleteGear(gear); + exit + end; HedgehogChAngle(HHGear); gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); @@ -3575,6 +3594,7 @@ if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then begin + HHGear^.State := HHGear^.State and (not gstNotKickable); DeleteGear(Gear); AfterAttack end @@ -3631,7 +3651,7 @@ else if Gear^.Angle < 2048 then inc(Gear^.Angle) else fChanged := false - end + end else begin if ((Gear^.Message and gmLeft) <> 0) then @@ -3706,6 +3726,7 @@ AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, dY, 0); AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, -dY, 0); end; + if HHGear <> nil then HHGear^.State := HHGear^.State and (not gstNotKickable); DeleteGear(Gear) end; @@ -4962,6 +4983,11 @@ begin AllInactive := false; HHGear := Gear^.Hedgehog^.Gear; + if HHGear = nil then + begin + DeleteGear(gear); + exit + end; HedgehogChAngle(HHGear); gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); @@ -5014,6 +5040,7 @@ if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then begin + HHGear^.State := HHGear^.State and (not gstNotKickable); DeleteGear(Gear); AfterAttack end @@ -5049,6 +5076,11 @@ begin AllInactive := false; HHGear := Gear^.Hedgehog^.Gear; + if HHGear = nil then + begin + DeleteGear(gear); + exit + end; HedgehogChAngle(HHGear); gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); @@ -5091,6 +5123,7 @@ 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); + HHGear^.State := HHGear^.State and (not gstNotKickable); DeleteGear(Gear); AfterAttack end diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uGearsHedgehog.pas --- a/hedgewars/uGearsHedgehog.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uGearsHedgehog.pas Thu Apr 02 21:09:56 2015 +0300 @@ -222,7 +222,7 @@ with Gear^, Gear^.Hedgehog^ do begin - if ((State and gstHHDriven) <> 0) and ((State and (gstAttacked or gstHHChooseTarget)) = 0) and (((State and gstMoving) = 0) + if ((State and gstHHDriven) <> 0) and ((State and (gstAttacked or gstChooseTarget)) = 0) and (((State and gstMoving) = 0) or (Power > 0) or (CurAmmoType = amTeleport) or @@ -1318,7 +1318,7 @@ begin if Gear^.Timer = 0 then begin - Gear^.State:= Gear^.State and (not (gstWait or gstLoser or gstWinner or gstAttacked or gstNotKickable or gstHHChooseTarget)); + Gear^.State:= Gear^.State and (not (gstWait or gstLoser or gstWinner or gstAttacked or gstNotKickable or gstChooseTarget)); if Gear^.Hedgehog^.Effects[heFrozen] = 0 then Gear^.Active:= false; AddCI(Gear); exit diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uGearsList.pas --- a/hedgewars/uGearsList.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uGearsList.pas Thu Apr 02 21:09:56 2015 +0300 @@ -41,6 +41,7 @@ (* gtHedgehog *) , amNothing (* gtMine *) , amMine (* gtCase *) , amNothing +(* gtAirMine *) , amAirMine (* gtExplosives *) , amNothing (* gtGrenade *) , amGrenade (* gtShell *) , amBazooka @@ -103,7 +104,6 @@ (* gtAddAmmo *) , amNothing (* gtGenericFaller *) , amNothing (* gtKnife *) , amKnife -(* gtAirMine *) , amAirMine ); @@ -166,6 +166,7 @@ function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear; var gear: PGear; //c: byte; + cakeData: PCakeData; begin inc(GCounter); @@ -193,6 +194,7 @@ gear^.AmmoType:= GearKindAmmoTypeMap[Kind]; gear^.CollisionMask:= $FFFF; gear^.Tint:= $FFFFFFFF; +gear^.Data:= nil; if CurrentHedgehog <> nil then begin @@ -342,6 +344,8 @@ gear^.Radius:= 3; gear^.Friction:= _450 * _0_01 * cRopePercent; RopePoints.Count:= 0; + gear^.Tint:= $D8D8D8FF; + gear^.Tag:= 0; // normal rope render end; gtMine: begin gear^.ImpactSound:= sndMineImpact; @@ -364,14 +368,14 @@ gear^.ImpactSound:= sndDenied; gear^.nImpactSounds:= 1; gear^.Health:= 30; - gear^.State:= gear^.State or gstMoving or gstNoGravity; + gear^.State:= gear^.State or gstMoving or gstNoGravity or gstSubmersible; gear^.Radius:= 8; gear^.Elasticity:= _0_55; gear^.Friction:= _0_995; gear^.Density:= _1; gear^.Angle:= 175; // Radius at which air bombs will start "seeking". $FFFFFFFF = unlimited. check is skipped. gear^.Power:= cMaxWindSpeed.QWordValue div 2; // hwFloat converted. 1/2 g default. defines the "seek" speed when a gear is in range. - gear^.Pos:= cMaxWindSpeed.QWordValue; // air friction. slows it down when not hitting stuff + gear^.Pos:= cMaxWindSpeed.QWordValue * 3 div 2; // air friction. slows it down when not hitting stuff gear^.Karma:= 30; // damage if gear^.Timer = 0 then begin @@ -500,12 +504,14 @@ gear^.Health:= 2048; gear^.Radius:= 7; gear^.Z:= cOnHHZ; - gear^.RenderTimer:= true; + gear^.RenderTimer:= false; gear^.DirAngle:= -90 * hwSign(Gear^.dX); if not dX.isNegative then gear^.Angle:= 1 else - gear^.Angle:= 3 + gear^.Angle:= 3; + New(cakeData); + gear^.Data:= Pointer(cakeData); end; gtHellishBomb: begin gear^.ImpactSound:= sndHellishImpact1; @@ -658,6 +664,7 @@ var team: PTeam; t,i: Longword; k: boolean; + cakeData: PCakeData; begin ScriptCall('onGearDelete', gear^.uid); @@ -673,6 +680,12 @@ if (Gear^.LinkedGear^.LinkedGear = Gear) then Gear^.LinkedGear^.LinkedGear:= nil; end +else if Gear^.Kind = gtCake then + begin + cakeData:= PCakeData(Gear^.Data); + Dispose(cakeData); + cakeData:= nil; + end else if Gear^.Kind = gtHedgehog then (* This behaviour dates back to revision 4, and I accidentally encountered it with TARDIS. I don't think it must apply to any modern weapon, since if it was actually hit, the best the gear could do would be to destroy itself immediately, and you'd still end up with two graves. I believe it should be removed diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uGearsRender.pas --- a/hedgewars/uGearsRender.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uGearsRender.pas Thu Apr 02 21:09:56 2015 +0300 @@ -36,6 +36,7 @@ rounded : array[0..MAXROPEPOINTS + 2] of TVertex2f; end; procedure RenderGear(Gear: PGear; x, y: LongInt); +procedure DrawHHOrder(); var RopePoints: record Count: Longword; @@ -68,7 +69,8 @@ EnableTexture(false); //glEnable(GL_LINE_SMOOTH); - Tint($70, $70, $70, $FF); + + Tint(Gear^.Tint shr 24 div 3, Gear^.Tint shr 16 and $FF div 3, Gear^.Tint shr 8 and $FF div 3, Gear^.Tint and $FF); n:= RopePoints.Count + 2; @@ -79,7 +81,7 @@ glLineWidth(3.0 * cScaleFactor); glDrawArrays(GL_LINE_STRIP, 0, n); - Tint($D8, $D8, $D8, $FF); + Tint(Gear^.Tint); glLineWidth(2.0 * cScaleFactor); glDrawArrays(GL_LINE_STRIP, 0, n); @@ -169,7 +171,7 @@ var roplen, i: LongInt; begin if Gear^.Hedgehog^.Gear = nil then exit; - if (cReducedQuality and rqSimpleRope) <> 0 then + if (Gear^.Tag = 1) or ((cReducedQuality and rqSimpleRope) <> 0) then DrawRopeLinesRQ(Gear) else begin @@ -214,6 +216,51 @@ end; end; +procedure DrawHHOrder(); +var HHGear: PGear; + hh: PHedgehog; + c, i, t, x, y, sprH, sprW, fSprOff: LongInt; +begin +t:= LocalTeam; + +if not CurrentTeam^.ExtDriven then + for i:= 0 to Pred(TeamsCount) do + if (TeamsArray[i] = CurrentTeam) then + t:= i; + +if t < 0 then + exit; + +if TeamsArray[t] <> nil then + begin + sprH:= SpritesData[sprBigDigit].Height; + sprW:= SpritesData[sprBigDigit].Width; + fSprOff:= sprW div 4 + SpritesData[sprFrame].Width div 4 - 1; // - 1 for overlap to avoid artifacts + i:= 0; + c:= 0; + repeat + hh:= @TeamsArray[t]^.Hedgehogs[i]; + inc(i); + if (hh <> nil) and (hh^.Gear <> nil) then + begin + inc(c); + HHGear:= hh^.Gear; + x:= hwRound(HHGear^.X) + WorldDx; + y:= hwRound(HHGear^.Y) + WorldDy - 2; + if (SpeechHogNumber <> c) or ((RealTicks and 512) < 256) then + begin + DrawTextureF(SpritesData[sprFrame].Texture, 0.5, x - fSprOff, y, 0, 1, SpritesData[sprFrame].Width, SpritesData[sprFrame].Height); + DrawTextureF(SpritesData[sprFrame].Texture, 0.5, x + fSprOff, y, 1, 1, SpritesData[sprFrame].Width, SpritesData[sprFrame].Height); + DrawTextureF(SpritesData[sprBigDigit].Texture, 0.5, x, y, c, 1, sprW, sprH); + end + else + DrawCircle(x, y, 20, 3, 0, $FF, $FF, $80); + end; + until (i > cMaxHHIndex); + end + +end; + procedure DrawHH(Gear: PGear; ox, oy: LongInt); var i, t: LongInt; @@ -1128,7 +1175,7 @@ end else if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) then // mine is chasing a hog DrawSprite(sprAirMine, x-16, y-16, (RealTicks div 25) mod 16) - else if Gear^.State and gstHHChooseTarget <> 0 then // mine is seeking for hogs + else if Gear^.State and gstChooseTarget <> 0 then // mine is seeking for hogs DrawSprite(sprAirMine, x-16, y-16, (RealTicks div 125) mod 16) else DrawSprite(sprAirMine, x-16, y-16, 4); // mine is active but not seeking diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uGearsUtils.pas --- a/hedgewars/uGearsUtils.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uGearsUtils.pas Thu Apr 02 21:09:56 2015 +0300 @@ -82,6 +82,7 @@ vg: PVisualGear; i, cnt: LongInt; wrap: boolean; + bubble: PVisualGear; begin if Radius > 4 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')'); if Radius > 25 then KickFlakes(Radius, X, Y); @@ -89,7 +90,17 @@ if ((Mask and EXPLNoGfx) = 0) then begin vg:= nil; - if Radius > 50 then vg:= AddVisualGear(X, Y, vgtBigExplosion) + if CheckCoordInWater(X, Y - Radius) then + begin + cnt:= 2 * Radius; + for i:= (Radius * Radius) div 4 downto 0 do + begin + bubble := AddVisualGear(X - Radius + random(cnt), Y - Radius + random(cnt), vgtBubble); + if bubble <> nil then + bubble^.dY:= 0.1 + random(20)/10; + end + end + else if Radius > 50 then vg:= AddVisualGear(X, Y, vgtBigExplosion) else if Radius > 10 then vg:= AddVisualGear(X, Y, vgtExplosion); if vg <> nil then vg^.Tint:= Tint; @@ -703,11 +714,7 @@ ScriptCall('onGearWaterSkip', Gear^.uid); end else - begin - if (not ((Gear^.Kind = gtJetpack) or (Gear^.Kind = gtBee))) then - Gear^.State:= (Gear^.State and (not gstSubmersible)); // making it temporary for most gears is more attractive I think CheckGearDrowning := false - end end; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uIO.pas --- a/hedgewars/uIO.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uIO.pas Thu Apr 02 21:09:56 2015 +0300 @@ -143,6 +143,7 @@ ParseChatCommand('chatmsg ' + #4, s, 2) else isProcessed:= false; + 'Y': ChatPasteBuffer:= copy(s, 2, Length(s) - 1); else isProcessed:= false; end; @@ -370,7 +371,11 @@ isInLag:= (headcmd = nil) and tmpflag and (not CurrentTeam^.hasGone); -if isInLag then fastUntilLag:= false +if isInLag and fastUntilLag then +begin + ParseCommand('spectate 0', true); + fastUntilLag:= false +end; end; procedure chFatalError(var s: shortstring); @@ -398,7 +403,7 @@ with CurrentHedgehog^.Gear^, CurrentHedgehog^ do - if (State and gstHHChooseTarget) <> 0 then + if (State and gstChooseTarget) <> 0 then begin isCursorVisible:= false; if not CurrentTeam^.ExtDriven then @@ -421,7 +426,7 @@ TargetPoint.Y:= putY end; AddFileLog('put: ' + inttostr(TargetPoint.X) + ', ' + inttostr(TargetPoint.Y)); - State:= State and (not gstHHChooseTarget); + State:= State and (not gstChooseTarget); if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackingPut) <> 0 then Message:= Message or (gmAttack and InputMask); end diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uLandGraphics.pas --- a/hedgewars/uLandGraphics.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uLandGraphics.pas Thu Apr 02 21:09:56 2015 +0300 @@ -82,7 +82,7 @@ inc(drawPixelBG); end else if ((Land[landY, landX] and lfObject) <> 0) or (((LandPixels[pixelY, pixelX] and AMask) shr AShift) < 255) then - LandPixels[pixelY, pixelX]:= LandPixels[pixelY, pixelX] and (not AMASK) + LandPixels[pixelY, pixelX]:= ExplosionBorderColorNoA end; end; @@ -199,7 +199,7 @@ begin calculatePixelsCoordinates(i, y, px, py); if ((Land[y, i] and lfIndestructible) = 0) and (not disableLandBack or (Land[y, i] > 255)) then - LandPixels[py, px]:= LandPixels[py, px] and (not AMASK); + LandPixels[py, px]:= ExplosionBorderColorNoA; end; icePixel: for i:= fromPix to toPix do @@ -920,7 +920,68 @@ Despeckle:= false end; +// a bit of AA for explosions procedure Smooth(X, Y: LongInt); +var c, r, g, b, a, i: integer; + nx, ny: LongInt; + pixel: LongWord; +begin + +// only AA inwards +if (Land[Y, X] and lfDamaged) = 0 then + exit; + +// check location +if (Y <= LongInt(topY) + 1) or (Y >= LAND_HEIGHT-2) +or (X <= LongInt(leftX) + 1) or (X >= LongInt(rightX) - 1) then + exit; + +// counter for neighbor pixels that are not known to be undamaged +c:= 8; + +// accumalating rgba value of relevant pixels here +r:= 0; +g:= 0; +b:= 0; +a:= 0; + +// iterate over all neighbor pixels (also itself, will be skipped anyway) +for nx:= X-1 to X+1 do + for ny:= Y-1 to Y+1 do + // only consider undamaged neighbors (also leads to skipping itself) + if (Land[ny, nx] and lfDamaged) = 0 then + begin + pixel:= LandPixels[ny, nx]; + inc(r, (pixel and RMask) shr RShift); + inc(g, (pixel and GMask) shr GShift); + inc(b, (pixel and BMask) shr BShift); + inc(a, (pixel and AMask) shr AShift); + dec(c); + end; + +// nothing do to if all neighbors damaged +if c < 1 then + exit; + +// use explosion color for damaged pixels +for i:= 1 to c do + begin + inc(r, ExplosionBorderColorR); + inc(g, ExplosionBorderColorG); + inc(b, ExplosionBorderColorB); + inc(a, 255); + end; + +// set resulting color value based on average of all neighbors +r:= r div 8; +g:= g div 8; +b:= b div 8; +a:= a div 8; +LandPixels[y,x]:= (r shl RShift) or (g shl GShift) or (b shl BShift) or (a shl AShift); + +end; + +procedure Smooth_oldImpl(X, Y: LongInt); begin // a bit of AA for explosions if (Land[Y, X] = 0) and (Y > LongInt(topY) + 1) and @@ -939,12 +1000,14 @@ (((((LandPixels[y,x] and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or (((((LandPixels[y,x] and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift) end; +{ if (Land[y, x-1] = lfObject) then Land[y,x]:= lfObject else if (Land[y, x+1] = lfObject) then Land[y,x]:= lfObject else Land[y,x]:= lfBasic; +} end else if ((((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0)) or (((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0)) @@ -965,6 +1028,7 @@ (((((LandPixels[y,x] and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or (((((LandPixels[y,x] and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift) end; +{ if (Land[y, x-1] = lfObject) then Land[y, x]:= lfObject else if (Land[y, x+1] = lfObject) then @@ -974,6 +1038,7 @@ else if (Land[y-1, x] = lfObject) then Land[y, x]:= lfObject else Land[y,x]:= lfBasic +} end end else if ((cReducedQuality and rqBlurryLand) = 0) and ((LandPixels[Y, X] and AMask) = AMask) @@ -1060,16 +1125,18 @@ end; end; -for y:= 0 to LAND_HEIGHT div 32 - 1 do - for x:= 0 to LAND_WIDTH div 32 - 1 do - if LandDirty[y, x] <> 0 then - begin - ty:= y * 32; - tx:= x * 32; - for yy:= ty to ty + 31 do - for xx:= tx to tx + 31 do - Smooth(xx,yy) - end; +// smooth explosion borders (except if land is blurry) +if (cReducedQuality and rqBlurryLand) = 0 then + for y:= 0 to LAND_HEIGHT div 32 - 1 do + for x:= 0 to LAND_WIDTH div 32 - 1 do + if LandDirty[y, x] <> 0 then + begin + ty:= y * 32; + tx:= x * 32; + for yy:= ty to ty + 31 do + for xx:= tx to tx + 31 do + Smooth(xx,yy) + end; for y:= 0 to LAND_HEIGHT div 32 - 1 do for x:= 0 to LAND_WIDTH div 32 - 1 do diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uLandObjects.pas --- a/hedgewars/uLandObjects.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uLandObjects.pas Thu Apr 02 21:09:56 2015 +0300 @@ -553,7 +553,12 @@ c2.g:= t; c2.b:= t end; - ExplosionBorderColor:= (c2.r shl RShift) or (c2.g shl GShift) or (c2.b shl BShift) or AMask; + ExplosionBorderColorR:= c2.r; + ExplosionBorderColorG:= c2.g; + ExplosionBorderColorB:= c2.b; + ExplosionBorderColorNoA:= + (c2.r shl RShift) or (c2.g shl GShift) or (c2.b shl BShift); + ExplosionBorderColor:= ExplosionBorderColorNoA or AMask; end else if key = 'water-top' then begin diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uRender.pas --- a/hedgewars/uRender.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uRender.pas Thu Apr 02 21:09:56 2015 +0300 @@ -48,6 +48,7 @@ procedure DrawCircle (X, Y, Radius, Width: LongInt); procedure DrawCircle (X, Y, Radius, Width: LongInt; r, g, b, a: Byte); +procedure DrawCircleFilled (X, Y, Radius: LongInt; r, g, b, a: Byte); procedure DrawLine (X0, Y0, X1, Y1, Width: Single; color: LongWord); inline; procedure DrawLine (X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte); @@ -59,12 +60,16 @@ procedure DrawWaves (Dir, dX, dY, oX: LongInt; tnt: Byte); procedure RenderClear (); -procedure RenderSetClearColor (r, g, b, a: real); +procedure RenderClear (mode: TRenderMode); +procedure RenderSetClearColor (r, g, b, a: real); procedure Tint (r, g, b, a: Byte); inline; procedure Tint (c: Longword); inline; procedure untint(); inline; procedure setTintAdd (f: boolean); inline; +// call this to finish the rendering of current frame +procedure FinishRender(); + function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; inline; // 0 => not offscreen, <0 => left/top of screen >0 => right/below of screen @@ -74,13 +79,14 @@ procedure SetScale(f: GLfloat); procedure UpdateViewLimits(); -procedure RenderSetup(); +procedure RendererSetup(); +procedure RendererCleanup(); + +procedure ChangeDepth(rm: TRenderMode; d: GLfloat); +procedure ResetDepth(rm: TRenderMode); // TODO everything below this should not need a public interface -procedure CreateFramebuffer(var frame, depth, tex: GLuint); -procedure DeleteFramebuffer(var frame, depth, tex: GLuint); - procedure EnableTexture(enable:Boolean); procedure SetTexCoordPointer(p: Pointer;n: Integer); inline; @@ -89,14 +95,9 @@ procedure UpdateModelviewProjection(); inline; -procedure openglLoadIdentity (); inline; -procedure openglTranslProjMatrix(X, Y, Z: GLFloat); inline; procedure openglPushMatrix (); inline; procedure openglPopMatrix (); inline; procedure openglTranslatef (X, Y, Z: GLfloat); inline; -procedure openglScalef (ScaleX, ScaleY, ScaleZ: GLfloat); inline; -procedure openglRotatef (RotX, RotY, RotZ: GLfloat; dir: LongInt); inline; -procedure openglTint (r, g, b, a: Byte); inline; implementation @@ -123,6 +124,20 @@ LastColorPointerN, LastTexCoordPointerN, LastVertexPointerN: Integer; {$ENDIF} +{$IFDEF USE_S3D_RENDERING} + // texture/vertex buffers for left/right/default eye modes + texLRDtb, texLvb, texRvb: array [0..3] of TVertex2f; +{$ENDIF} + +procedure openglLoadIdentity (); forward; +procedure openglTranslProjMatrix(X, Y, Z: GLFloat); forward; +procedure openglScalef (ScaleX, ScaleY, ScaleZ: GLfloat); forward; +procedure openglRotatef (RotX, RotY, RotZ: GLfloat; dir: LongInt); forward; +procedure openglTint (r, g, b, a: Byte); forward; + +procedure CreateFramebuffer(var frame, depth, tex: GLuint); forward; +procedure DeleteFramebuffer(var frame, depth, tex: GLuint); forward; + function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; inline; begin isAreaOffscreen:= (isDxAreaOffscreen(X, Width) <> 0) or (isDyAreaOffscreen(Y, Height) <> 0); @@ -147,11 +162,92 @@ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); end; +{$IFDEF USE_S3D_RENDERING} +procedure RenderClear(mode: TRenderMode); +var frame: GLuint; +begin + if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) then + begin + case mode of + rmLeftEye: frame:= frameL; + rmRightEye: frame:= frameR; + else + frame:= defaultFrame; + end; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frame); + + RenderClear(); + end + else + begin + // draw left eye in red channel only + if mode = rmLeftEye then + begin + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + RenderClear(); + if cStereoMode = smGreenRed then + glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE) + else if cStereoMode = smBlueRed then + glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE) + else if cStereoMode = smCyanRed then + glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE) + else + glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE); + end + else + begin + // draw right eye in selected channel(s) only + if cStereoMode = smRedGreen then + glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE) + else if cStereoMode = smRedBlue then + glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE) + else if cStereoMode = smRedCyan then + glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE) + else + glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE); + end; + end; +end; +{$ENDIF} + procedure RenderSetClearColor(r, g, b, a: real); begin glClearColor(r, g, b, a); end; +procedure FinishRender(); +begin + +{$IFDEF USE_S3D_RENDERING} +if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) then + begin + RenderClear(rmDefault); + + SetScale(cDefaultZoomLevel); + + + // same for all + SetTexCoordPointer(@texLRDtb, Length(texLRDtb)); + + + // draw left frame + glBindTexture(GL_TEXTURE_2D, texl); + SetVertexPointer(@texLvb, Length(texLvb)); + //UpdateModelviewProjection; + glDrawArrays(GL_TRIANGLE_FAN, 0, Length(texLvb)); + + // draw right frame + glBindTexture(GL_TEXTURE_2D, texl); + SetVertexPointer(@texRvb, Length(texRvb)); + //UpdateModelviewProjection; + glDrawArrays(GL_TRIANGLE_FAN, 0, Length(texRvb)); + + SetScale(zoom); + end; +{$ENDIF} +end; + {$IFDEF GL2} function CompileShader(shaderFile: string; shaderType: GLenum): GLuint; var @@ -302,14 +398,42 @@ glDeleteRenderbuffersEXT(1, @depth); glDeleteFramebuffersEXT(1, @frame); end; +{$ENDIF} +procedure RendererCleanup(); +begin +{$IFNDEF PAS2C} +{$IFDEF USE_VIDEO_RECORDING} + if defaultFrame <> 0 then + DeleteFramebuffer(defaultFrame, depthv, texv); {$ENDIF} -procedure RenderSetup(); +{$IFDEF USE_S3D_RENDERING} + if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) then + begin + DeleteFramebuffer(framel, depthl, texl); + DeleteFramebuffer(framer, depthr, texr); + end +{$ENDIF} +{$ENDIF} +end; + +procedure RendererSetup(); var AuxBufNum: LongInt = 0; tmpstr: ansistring; tmpint: LongInt; tmpn: LongInt; begin +{$IFDEF MOBILE} + // TODO: this function creates an opengles1.1 context + // un-comment below and add proper logic to support opengles2.0 + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + if SDLGLcontext = nil then + SDLGLcontext:= SDL_GL_CreateContext(SDLwindow); + SDLTry(SDLGLcontext <> nil, true); + SDL_GL_SetSwapInterval(1); +{$ENDIF} + // suppress hint/warning AuxBufNum:= AuxBufNum; @@ -426,12 +550,66 @@ CreateFramebuffer(framel, depthl, texl); CreateFramebuffer(framer, depthr, texr); + + + // reset glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame) end else cStereoMode:= smNone; end; + + // set up vertex/texture buffers for frame textures + texLRDtb[0].X:= 0.0; + texLRDtb[0].Y:= 0.0; + texLRDtb[1].X:= 1.0; + texLRDtb[1].Y:= 0.0; + texLRDtb[2].X:= 1.0; + texLRDtb[2].Y:= 1.0; + texLRDtb[3].X:= 0.0; + texLRDtb[3].Y:= 1.0; + + if cStereoMode = smHorizontal then + begin + texLvb[0].X:= cScreenWidth / -2; + texLvb[0].Y:= cScreenHeight; + texLvb[1].X:= 0; + texLvb[1].Y:= cScreenHeight; + texLvb[2].X:= 0; + texLvb[2].Y:= 0; + texLvb[3].X:= cScreenWidth / -2; + texLvb[3].Y:= 0; + + texRvb[0].X:= 0; + texRvb[0].Y:= cScreenHeight; + texRvb[1].X:= cScreenWidth / 2; + texRvb[1].Y:= cScreenHeight; + texRvb[2].X:= cScreenWidth / 2; + texRvb[2].Y:= 0; + texRvb[3].X:= 0; + texRvb[3].Y:= 0; + end + else + begin + texLvb[0].X:= cScreenWidth / -2; + texLvb[0].Y:= cScreenHeight / 2; + texLvb[1].X:= cScreenWidth / 2; + texLvb[1].Y:= cScreenHeight / 2; + texLvb[2].X:= cScreenWidth / 2; + texLvb[2].Y:= 0; + texLvb[3].X:= cScreenWidth / -2; + texLvb[3].Y:= 0; + + texRvb[0].X:= cScreenWidth / -2; + texRvb[0].Y:= cScreenHeight; + texRvb[1].X:= cScreenWidth / 2; + texRvb[1].Y:= cScreenHeight; + texRvb[2].X:= cScreenWidth / 2; + texRvb[2].Y:= cScreenHeight / 2; + texRvb[3].X:= cScreenWidth / -2; + texRvb[3].Y:= cScreenHeight / 2; + end; {$ENDIF} // set view port to whole window @@ -1220,6 +1398,25 @@ glDisable(GL_LINE_SMOOTH); end; +procedure DrawCircleFilled(X, Y, Radius: LongInt; r, g, b, a: Byte); +var + i: LongInt; +begin + VertexBuffer[0].X := X; + VertexBuffer[0].Y := Y; + + for i := 1 to 19 do begin + VertexBuffer[i].X := X + Radius*cos(i*pi/9); + VertexBuffer[i].Y := Y + Radius*sin(i*pi/9); + end; + + EnableTexture(False); + Tint(r, g, b, a); + SetVertexPointer(@VertexBuffer[0], 20); + glDrawArrays(GL_TRIANGLE_FAN, 0, 20); + Untint(); + EnableTexture(True); +end; procedure DrawHedgehog(X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real); const VertexBuffer: array [0..3] of TVertex2f = ( @@ -1387,11 +1584,6 @@ firsti:= -1; afteri:= 0; -if GameTicks < 2000 then - lol:= 2000 - GameTicks -else - lol:= 0; - if InTopY < 0 then InTopY:= 0; @@ -1403,6 +1595,13 @@ end else begin + + // animate water walls raise animation at start of game + if GameTicks < 2000 then + lol:= 2000 - GameTicks + else + lol:= 0; + if InLeftX > ViewLeftX then begin VertexBuffer[0].X:= OutLeftX - lol; @@ -1712,6 +1911,42 @@ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); end; +procedure ChangeDepth(rm: TRenderMode; d: GLfloat); +var tmp: LongInt; +begin +{$IFNDEF USE_S3D_RENDERING} + rm:= rm; d:= d; tmp:= tmp; // avoid hint +{$ELSE} + d:= d / 5; + if rm = rmDefault then + exit + else if rm = rmLeftEye then + d:= -d; + cStereoDepth:= cStereoDepth + d; + openglTranslProjMatrix(d, 0, 0); + tmp:= round(d / cScaleFactor * cScreenWidth); + ViewLeftX := ViewLeftX - tmp; + ViewRightX:= ViewRightX - tmp; +{$ENDIF} +end; + +procedure ResetDepth(rm: TRenderMode); +var tmp: LongInt; +begin +{$IFNDEF USE_S3D_RENDERING} + rm:= rm; tmp:= tmp; // avoid hint +{$ELSE} + if rm = rmDefault then + exit; + openglTranslProjMatrix(-cStereoDepth, 0, 0); + tmp:= round(cStereoDepth / cScaleFactor * cScreenWidth); + ViewLeftX := ViewLeftX + tmp; + ViewRightX:= ViewRightX + tmp; + cStereoDepth:= 0; +{$ENDIF} +end; + + procedure initModule; begin LastTint:= cWhiteColor + 1; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uRenderUtils.pas --- a/hedgewars/uRenderUtils.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uRenderUtils.pas Thu Apr 02 21:09:56 2015 +0300 @@ -171,7 +171,7 @@ r0:= (r0 * (255 - LongInt(a1)) + r1 * LongInt(a1)) div 255; g0:= (g0 * (255 - LongInt(a1)) + g1 * LongInt(a1)) div 255; b0:= (b0 * (255 - LongInt(a1)) + b1 * LongInt(a1)) div 255; - a0:= (a0 * (255 - LongInt(a1)) + a1 * LongInt(a1)) div 255; + a0:= a0 + ((255 - LongInt(a0)) * a1 div 255); destPixels^[i]:= SDL_MapRGBA(dest^.format, r0, g0, b0, a0); end; end; diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uScript.pas --- a/hedgewars/uScript.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uScript.pas Thu Apr 02 21:09:56 2015 +0300 @@ -2309,7 +2309,7 @@ begin if CheckLuaParamCount(L, 5, 'TestRectForObstacle', 'x1, y1, x2, y2, landOnly') then begin - rtn:= TestRectancleForObstacle( + rtn:= TestRectangleForObstacle( lua_tointeger(L, 1), lua_tointeger(L, 2), lua_tointeger(L, 3), @@ -3027,7 +3027,7 @@ ScriptSetInteger('gstAttacked' , gstAttacked); ScriptSetInteger('gstAttacking' , gstAttacking); ScriptSetInteger('gstCollision' , gstCollision); -ScriptSetInteger('gstHHChooseTarget', gstHHChooseTarget); +ScriptSetInteger('gstChooseTarget', gstChooseTarget); ScriptSetInteger('gstHHJumping' , gstHHJumping); ScriptSetInteger('gsttmpFlag' , gsttmpFlag); ScriptSetInteger('gstHHThinking' , gstHHThinking); diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uStore.pas --- a/hedgewars/uStore.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uStore.pas Thu Apr 02 21:09:56 2015 +0300 @@ -561,19 +561,8 @@ end; end; end; -{$IFNDEF PAS2C} -{$IFDEF USE_VIDEO_RECORDING} - if defaultFrame <> 0 then - DeleteFramebuffer(defaultFrame, depthv, texv); -{$ENDIF} -{$IFDEF USE_S3D_RENDERING} - if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) then - begin - DeleteFramebuffer(framel, depthl, texl); - DeleteFramebuffer(framer, depthr, texr); - end -{$ENDIF} -{$ENDIF} + +RendererCleanup(); end; @@ -756,18 +745,7 @@ AddFileLog('Setting up OpenGL (using driver: ' + shortstring(SDL_VideoDriverName(buf, sizeof(buf))) + ')'); {$ENDIF} -{$IFDEF MOBILE} - // TODO: this function creates an opengles1.1 context - // un-comment below and add proper logic to support opengles2.0 - //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - if SDLGLcontext = nil then - SDLGLcontext:= SDL_GL_CreateContext(SDLwindow); - SDLTry(SDLGLcontext <> nil, true); - SDL_GL_SetSwapInterval(1); -{$ENDIF} - - RenderSetup(); + RendererSetup(); end; (* @@ -1045,7 +1023,7 @@ glutHideWindow(); // we do not need to set this callback, but it is required for GLUT3 compat glutDisplayFunc(@SwapBuffers); - RenderSetup(); + RendererSetup(); end; {$ENDIF} // SDL2 {$ENDIF} // USE_VIDEO_RECORDING diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uTeams.pas --- a/hedgewars/uTeams.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uTeams.pas Thu Apr 02 21:09:56 2015 +0300 @@ -549,10 +549,12 @@ Gear: PGear; begin s:= ''; -if (not isDeveloperMode) or (CurrentTeam = nil) then +if (not isDeveloperMode) then exit; +TryDo((CurrentTeam <> nil), 'Can''t add hedgehogs yet, add a team first!', true); with CurrentTeam^ do begin + TryDo(HedgehogsNumber<=cMaxHHIndex, 'Can''t add hedgehog to "' + TeamName + '"! (already ' + intToStr(HedgehogsNumber) + ' hogs)', true); SplitBySpace(id, s); SwitchCurrentHedgehog(@Hedgehogs[HedgehogsNumber]); CurrentHedgehog^.BotLevel:= StrToInt(id); diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uTypes.pas --- a/hedgewars/uTypes.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uTypes.pas Thu Apr 02 21:09:56 2015 +0300 @@ -92,20 +92,21 @@ ); // Gears that interact with other Gears and/or Land - TGearType = (gtFlame, gtHedgehog, gtMine, gtCase, gtExplosives, // these gears should be avoided when searching a spawn place - gtGrenade, gtShell, gtGrave, gtBee, // 8 - gtShotgunShot, gtPickHammer, gtRope, // 11 - gtDEagleShot, gtDynamite, gtClusterBomb, gtCluster, gtShover, // 16 - gtFirePunch, gtATStartGame, // 18 - gtATFinishGame, gtParachute, gtAirAttack, gtAirBomb, gtBlowTorch, // 23 - gtGirder, gtTeleport, gtSwitcher, gtTarget, gtMortar, // 28 - gtWhip, gtKamikaze, gtCake, gtSeduction, gtWatermelon, gtMelonPiece, // 34 - gtHellishBomb, gtWaterUp, gtDrill, gtBallGun, gtBall, gtRCPlane, // 40 - gtSniperRifleShot, gtJetpack, gtMolotov, gtBirdy, // 44 - gtEgg, gtPortal, gtPiano, gtGasBomb, gtSineGunShot, gtFlamethrower, // 50 - gtSMine, gtPoisonCloud, gtHammer, gtHammerHit, gtResurrector, // 55 + // first row of gears (= 10 do + begin + dec(Gear^.Timer, 10); + if WindBarWidth < Gear^.Tag then + inc(WindBarWidth) + else if WindBarWidth > Gear^.Tag then + dec(WindBarWidth); + end; + if cWindspeedf > Gear^.dAngle then + begin + cWindspeedf := cWindspeedf - Gear^.Angle*Steps; + if cWindspeedf < Gear^.dAngle then cWindspeedf:= Gear^.dAngle; + end + else if cWindspeedf < Gear^.dAngle then + begin + cWindspeedf := cWindspeedf + Gear^.Angle*Steps; + if cWindspeedf > Gear^.dAngle then cWindspeedf:= Gear^.dAngle; + end; + end; + + if ((WindBarWidth = Gear^.Tag) and (cWindspeedf = Gear^.dAngle)) or (currwindbar <> Gear) then + begin + if currwindbar = Gear then currwindbar:= nil; + DeleteVisualGear(Gear) + end +end; + procedure doStepSmoothWindBar(Gear: PVisualGear; Steps: Longword); begin -inc(Gear^.Timer, Steps); - -while Gear^.Timer >= 10 do - begin - dec(Gear^.Timer, 10); - if WindBarWidth < Gear^.Tag then - inc(WindBarWidth) - else if WindBarWidth > Gear^.Tag then - dec(WindBarWidth); - end; -if cWindspeedf > Gear^.dAngle then - begin - cWindspeedf := cWindspeedf - Gear^.Angle*Steps; - if cWindspeedf < Gear^.dAngle then cWindspeedf:= Gear^.dAngle; - end -else if cWindspeedf < Gear^.dAngle then - begin - cWindspeedf := cWindspeedf + Gear^.Angle*Steps; - if cWindspeedf > Gear^.dAngle then cWindspeedf:= Gear^.dAngle; - end; - -if (WindBarWidth = Gear^.Tag) and (cWindspeedf = Gear^.dAngle) then - DeleteVisualGear(Gear) + currwindbar:= Gear; + Gear^.doStep:= @doStepSmoothWindBarWork; + doStepSmoothWindBarWork(Gear, Steps) end; //////////////////////////////////////////////////////////////////////////////// procedure doStepStraightShot(Gear: PVisualGear; Steps: Longword); diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uVisualGearsList.pas --- a/hedgewars/uVisualGearsList.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uVisualGearsList.pas Thu Apr 02 21:09:56 2015 +0300 @@ -78,6 +78,7 @@ vgtSmokeTrace, vgtEvilTrace, vgtNote, + vgtFeather, vgtSmoothWindBar])) then exit; @@ -399,6 +400,7 @@ vgtSmallDamageTag, vgtHealthTag, vgtStraightShot, + vgtFeather, vgtChunk: gear^.Layer:= 3; // 2: this layer is outside the screen when stereo @@ -409,7 +411,6 @@ vgtSteam, vgtAmmo, vgtShell, - vgtFeather, vgtEgg, vgtBeeTrace, vgtSmokeRing, diff -r 1ff3dd3705b1 -r 99273b7afbff hedgewars/uWorld.pas --- a/hedgewars/uWorld.pas Mon Feb 16 22:33:15 2015 +0300 +++ b/hedgewars/uWorld.pas Thu Apr 02 21:09:56 2015 +0300 @@ -17,7 +17,6 @@ *) {$INCLUDE "options.inc"} -{$IF GLunit = GL}{$DEFINE GLunit:=GL,GLext}{$ENDIF} unit uWorld; interface @@ -53,7 +52,6 @@ , uVisualGears , uChat , uLandTexture - , GLunit , uVariables , uUtils , uTextures @@ -857,154 +855,28 @@ else ZoomValue:= zoom; - // Sky - glClear(GL_COLOR_BUFFER_BIT); - //glPushMatrix; - //glScalef(1.0, 1.0, 1.0); - if (not isPaused) and (not isAFK) and (GameType <> gmtRecord) then MoveCamera; if cStereoMode = smNone then begin - glClear(GL_COLOR_BUFFER_BIT); + RenderClear(); DrawWorldStereo(Lag, rmDefault) end {$IFDEF USE_S3D_RENDERING} - else if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) then + else begin - // create left fb - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framel); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + // draw frame for left eye + RenderClear(rmLeftEye); DrawWorldStereo(Lag, rmLeftEye); - // create right fb - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framer); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + // draw frame for right eye + RenderClear(rmRightEye); DrawWorldStereo(0, rmRightEye); - - // detatch drawing from fbs - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, defaultFrame); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); - SetScale(cDefaultZoomLevel); - - // draw left frame - glBindTexture(GL_TEXTURE_2D, texl); - glBegin(GL_QUADS); - if cStereoMode = smHorizontal then - begin - glTexCoord2f(0.0, 0.0); - glVertex2d(cScreenWidth / -2, cScreenHeight); - glTexCoord2f(1.0, 0.0); - glVertex2d(0, cScreenHeight); - glTexCoord2f(1.0, 1.0); - glVertex2d(0, 0); - glTexCoord2f(0.0, 1.0); - glVertex2d(cScreenWidth / -2, 0); - end - else - begin - glTexCoord2f(0.0, 0.0); - glVertex2d(cScreenWidth / -2, cScreenHeight / 2); - glTexCoord2f(1.0, 0.0); - glVertex2d(cScreenWidth / 2, cScreenHeight / 2); - glTexCoord2f(1.0, 1.0); - glVertex2d(cScreenWidth / 2, 0); - glTexCoord2f(0.0, 1.0); - glVertex2d(cScreenWidth / -2, 0); - end; - glEnd(); + end; +{$ENDIF} - // draw right frame - glBindTexture(GL_TEXTURE_2D, texr); - glBegin(GL_QUADS); - if cStereoMode = smHorizontal then - begin - glTexCoord2f(0.0, 0.0); - glVertex2d(0, cScreenHeight); - glTexCoord2f(1.0, 0.0); - glVertex2d(cScreenWidth / 2, cScreenHeight); - glTexCoord2f(1.0, 1.0); - glVertex2d(cScreenWidth / 2, 0); - glTexCoord2f(0.0, 1.0); - glVertex2d(0, 0); - end - else - begin - glTexCoord2f(0.0, 0.0); - glVertex2d(cScreenWidth / -2, cScreenHeight); - glTexCoord2f(1.0, 0.0); - glVertex2d(cScreenWidth / 2, cScreenHeight); - glTexCoord2f(1.0, 1.0); - glVertex2d(cScreenWidth / 2, cScreenHeight / 2); - glTexCoord2f(0.0, 1.0); - glVertex2d(cScreenWidth / -2, cScreenHeight / 2); - end; - glEnd(); - SetScale(zoom); - end - else - begin - // clear scene - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); - // draw left eye in red channel only - if cStereoMode = smGreenRed then - glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE) - else if cStereoMode = smBlueRed then - glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE) - else if cStereoMode = smCyanRed then - glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE) - else - glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE); - DrawWorldStereo(Lag, rmLeftEye); - // draw right eye in selected channel(s) only - if cStereoMode = smRedGreen then - glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE) - else if cStereoMode = smRedBlue then - glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE) - else if cStereoMode = smRedCyan then - glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE) - else - glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE); - DrawWorldStereo(Lag, rmRightEye); - end -{$ENDIF} -end; - -procedure ChangeDepth(rm: TRenderMode; d: GLfloat); -var tmp: LongInt; -begin -{$IFNDEF USE_S3D_RENDERING} - rm:= rm; d:= d; tmp:= tmp; // avoid hint -{$ELSE} - d:= d / 5; - if rm = rmDefault then - exit - else if rm = rmLeftEye then - d:= -d; - cStereoDepth:= cStereoDepth + d; - openglTranslProjMatrix(d, 0, 0); - tmp:= round(d / cScaleFactor * cScreenWidth); - ViewLeftX := ViewLeftX - tmp; - ViewRightX:= ViewRightX - tmp; -{$ENDIF} -end; - -procedure ResetDepth(rm: TRenderMode); -var tmp: LongInt; -begin -{$IFNDEF USE_S3D_RENDERING} - rm:= rm; tmp:= tmp; // avoid hint -{$ELSE} - if rm = rmDefault then - exit; - openglTranslProjMatrix(-cStereoDepth, 0, 0); - tmp:= round(cStereoDepth / cScaleFactor * cScreenWidth); - ViewLeftX := ViewLeftX + tmp; - ViewRightX:= ViewRightX + tmp; - cStereoDepth:= 0; -{$ENDIF} +FinishRender(); end; procedure RenderWorldEdge; @@ -1291,8 +1163,8 @@ tdx, tdy: Double; s: shortstring; offsetX, offsetY, screenBottom: LongInt; - VertexBuffer: array [0..3] of TVertex2f; replicateToLeft, replicateToRight, tmp: boolean; + a: Byte; begin if WorldEdge <> weWrap then begin @@ -1723,27 +1595,16 @@ end; if ScreenFade <> sfNone then begin + r.x:= ViewLeftX; + r.y:= ViewTopY; + r.w:= ViewWidth; + r.h:= ViewHeight; + case ScreenFade of - sfToBlack, sfFromBlack: Tint(0, 0, 0, ScreenFadeValue * 255 div 1000); - sfToWhite, sfFromWhite: Tint($FF, $FF, $FF, ScreenFadeValue * 255 div 1000); + sfToBlack, sfFromBlack: DrawRect(r, 0, 0, 0, ScreenFadeValue * 255 div 1000, true); + sfToWhite, sfFromWhite: DrawRect(r, $FF, $FF, $FF, ScreenFadeValue * 255 div 1000, true); end; - VertexBuffer[0].X:= -cScreenWidth; - VertexBuffer[0].Y:= cScreenHeight; - VertexBuffer[1].X:= -cScreenWidth; - VertexBuffer[1].Y:= 0; - VertexBuffer[2].X:= cScreenWidth; - VertexBuffer[2].Y:= 0; - VertexBuffer[3].X:= cScreenWidth; - VertexBuffer[3].Y:= cScreenHeight; - - EnableTexture(false); - - SetVertexPointer(@VertexBuffer[0], 4); - glDrawArrays(GL_TRIANGLE_FAN, 0, High(VertexBuffer) - Low(VertexBuffer) + 1); - - EnableTexture(true); - untint; if not isFirstFrame and ((ScreenFadeValue = 0) or (ScreenFadeValue = sfMax)) then ScreenFade:= sfNone end @@ -1764,15 +1625,11 @@ end; DrawTexture( -(cScreenWidth shr 1) + 50, 20, recTexture); + //a:= Byte(Round(127*(1 + sin(RealTicks*0.007)))); + a:= Byte(min(255, abs(-255 + ((RealTicks div 2) and 511)))); + // 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(); - untint; - glEnable(GL_TEXTURE_2D); + DrawCircleFilled(-(cScreenWidth shr 1) + 30, 35, 10, $FF, $00, $00, a); end; {$ENDIF} @@ -1808,7 +1665,7 @@ begin if not CurrentTeam^.ExtDriven then TargetCursorPoint:= CursorPoint; with CurrentHedgehog^ do - if (Gear <> nil) and ((Gear^.State and gstHHChooseTarget) <> 0) then + if (Gear <> nil) and ((Gear^.State and gstChooseTarget) <> 0) then begin if (CurAmmoType = amNapalm) or (CurAmmoType = amMineStrike) or (((GameFlags and gfMoreWind) <> 0) and ((CurAmmoType = amDrillStrike) or (CurAmmoType = amAirAttack))) then DrawLine(-3000, topY-300, 7000, topY-300, 3.0, (Team^.Clan^.Color shr 16), (Team^.Clan^.Color shr 8) and $FF, Team^.Clan^.Color and $FF, $FF); diff -r 1ff3dd3705b1 -r 99273b7afbff misc/OfficialChallenges/racer_#13.hwmap --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/OfficialChallenges/racer_#13.hwmap Thu Apr 02 21:09:56 2015 +0300 @@ -0,0 +1,1 @@ +AAAEC3icHZJ9aJZVGIevc859znme930etsRKEyllVlJr2AInGDXBBSWVfdHXlFFiq5CWH39U7gNCHVE6WrnSRIq0j01nW7KxaZEtwvFMSzYCl9MWKKnEYrI5puvZ+9/hwH39rt+5T1TjHybMcJjsjXoHJpKr+Dn2KG7KtOCV2oqZrdqZGmGiLX8uE+lJj7XlXdJjTF2S9C5PJgA32ZafcZP4s25d4rvdGXy5X4A77+/AbXHDuFVuA67GGdxp24lfadvx7XZ0eqIiN0Zwzr6WBL/Zvwi+cI8TPOcO4MfdFXy9vw0/wxfidvvZuCJ3AXvK7cJud0uwa+2v2FV2JXajnMZ+I29gr8pNuHLTjzthWvFl5lt8l+khWGCuEbwjjxF0yfcEg/bOXC7hqLyehP/IMGFinyXcY38hLHd3EUbuXYKDro+gzIf4fr8Iv9o/hLuQPp2r8iXYKT8T25iWtsVuJzLkSpFddghZa9cjyy3IYqlHlsoNyFNmN1Jj7ka6dQ821JXYl/Ut2ONqELdEHcQdUI34Bep9/GfqU4J81UmwWV1MTfU9hCW6jrBWnyXsMOnyhkw34X9yb64C8WG9OYm/1qPEdWYN8TLTS3RZbiXaJquJbpatZPfKR2QXyjYyHVJB5gmZT3jN9BEeMZWEjXqccMt0xoc6Q9ipmgjHVTGZFfxJ5hC7yc5jA9km1hDlUUVUx06i8wwQ368WElerD4j3q+vTLhtzQsR7dHES1+uviFeYPKJ/zfNE1aaWyJm3U5R5lGypniQb6QYyU3oO2blqP9kKtZzsMaWIFvM3UQsjxLPU7cSVqpr4Y3UpR0YWyWAi8+2TmDH7E+aQK8A846rQF90+9Cb3A+q660K95z5BFbgX4YQLoMF+DutsEbwiHVAry6DdDKAwb6EqzH2ok8ajy9Lfro8YwRSaIswOsx5zxiTITClBiqQlZ4DJV8cSPaZL0N/pJvQjuh/Vr4dRL+kfUaF+Ewa0g1Nqe5qh5qGepg/Vw5fopTSj9zGMHlEPTqPacjxMKFcSPWJfRTfb3tQlFVcn3SxUuR1DKdsMvbYUfpbjcFleQD0ggmpJe+gC8we6QUL0uXTTxsvvOR6+0Jcl0SZfhjpKaxLOoJX/Ac8c7Ww= \ No newline at end of file diff -r 1ff3dd3705b1 -r 99273b7afbff misc/OfficialChallenges/racer_#14.hwmap --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/OfficialChallenges/racer_#14.hwmap Thu Apr 02 21:09:56 2015 +0300 @@ -0,0 +1,1 @@ +AAAFlnicHZR3bNdlEMY/771371FLixoxKEKEQAgGwzA1SMJ0D6ZARY1SLTEgKEVGGOKIDGUjEJkBIYKADFFQ2RISLFVcjQXRGnBQLEJECUgUv/398+RyefO+994ztEgLsUt+FdI35JH/itxPwa+2g7yquIUGo9M89Lj0hTKWbKPMW9Ngdw5YkushN0rJNrnRxlJ4XQ6kJNcjdoy1ywtaxFr8F5Uq/1lb4if0PvxHHYYf1Vn4N7oZP6xf4fv1PL7DrsPXWxG+3Abgs20MPtkW4sNtGz7IsnN321m8XcrHb0itcEk9SDXpcdKRNIb0QZpFWpzWkCalT0hPpCOkbukk6eZ0gYQ7Vu2NsD3eClvmRdh4744N9J5YBy/GCv1J9LQ/gx70EegqL0Mn+Ri02Mei7X0cWj+r4m8+mvipjyQu9+HE8T6EOMCfILb3AcQCfwA57V2RQ94BecdbIK95Q6TUFbkr/Ym0SNVISocJNWk7oSKtJGxJ0wmL0kjCpFRMKE2dCQ+l5oTbUyI0sxpCgVXAP/Ye1NgcOGrPQ7n1ht3WFrZaIazVWlih5bBI18FcnQoztBSmaXeYok0ziJdhaqyC1+N2mBnfhPmxDBbHXrAqtoENMR+2Sw18Kp/Bl5JdVS3T4ZwMJYg8SGgotxJay9WELuE8YUCoIowIuwlTw2rCyvAGYWcYRfguPEb4K9yDXBvaI+1CE6RPyEdGcgmZRw2yjWNIJZ8jF9lHbMyHxK5sJD7NauI0lhE3soj4FfOJF5iDNmYW2q0OhjAbfYNMp5tZgH6baVIvshK7iXVYN7ZipezCpnEIe5dK7DAnsVrOkwpCJN0aGpIeCq1Jw0Jn0rTQj7QmDCXtDS+TjobFpPPhfTw/VODNwym8oyjeU5rhJdIFf0Eex1+TifgCWYq/LTvxzXIc3yn/4gdjU/yL2A2vjE/h38cp+E9xLX4ylmfWiGdy/shm0cIfUn3tQKqnA0lBx2MXdVk2qe7BftBqrEL/wz62Jtga64TNzExio+w5rNimYJ1sKdbYNqGXbQ96zD5HP7IqdKGdQEfZKbRX9m29xc6gyX4nnrDfiHvtJ+Iy+444IdtMHGS7iJ1sQ8aCvYVcsVeRkzYMKbe+GVGZOWWFNUJmaEbeBD2KDNePkBJdhDyio5F+2gfppW2QnloP6R1/Rh6O+5FH44rMAXEiUhYHIS/Fjhn78QZkjVxGPpEfka/lAFIr64kubxJbymTiXfIssVQeI06RnsS10oNYLncQ/5Db0GukHVokbdFB0h59UW5HV0pn9IDci/4i/bEkT2MtZTR2ZyZgGywrsImyHVsgR7D18ju2N+ZhX8csFE7E+7BzcSh2Oc4gxbiJlJfpLtWPf+c4yl7LiKqDuqp/uRbVram5Sjl7Mxp9uHQvz1uVKTGvii2VeVU+mAbjcsCWXI+rOqTqygbXB8ul7dm6tD2bf0SWVhTMtS5cOVWPff8D7Vs1fw== \ No newline at end of file diff -r 1ff3dd3705b1 -r 99273b7afbff misc/OfficialChallenges/racer_#15.hwmap --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/OfficialChallenges/racer_#15.hwmap Thu Apr 02 21:09:56 2015 +0300 @@ -0,0 +1,1 @@ +AAACYnicFc9NSJRRFAbgc3/PfP8lqGGLIlpIJVktlHAgXaSRLTLMQIQGwwhJF8UIUs7umHExvCn5CcWgiSFo0SBGUrjJjSAIFZWgQdmiggqjdd9sHs7qfc/rZ2UWwnasg6hS1IMzy7ZBn9BbAEfwEXAu6kBl2FNgGTZbcMpFZSEo5+0F+VLfKMiIbczrMbk2H7RpM8+kXs3zEdlR5BLoa6w77/TqK3nvuuzPh1/lmbxXxarzsCCHYr6i0zEe1Cux14k1RY4nHyRX0CLvx3Jc/IxTa6wpxlL+KQ4n4FsctfLGmLWIVstfiArL38GCVS3wzKppPmJTOXXYukdVifWW9CHr1fAd6+dg0uIyzFj+CmOry3DUph7iPevuxT7r7sc+iPZp3zKr7hjxRlUniE4jf8sfJnVA3zb4Tx4z6peIDS6xdeNe5OkiteB8hxnjZOSkcR/rdJFa8HNi0/hZ1WXCniQvNKLC+DmcMmIR75Ju1OeJN6sBYnk5TJAVVQQd0E+8DK4S32LTxFf5ZRLPYZzkEAyR/CAYiY8yR3BBPSD2Gp8UmQKOqEmcTVDncIpwFy4TliY7cRNHCf+oDCGpbUoNq8/kDKguwlviLekGUUp6B3LknCyyzrrJbU96vXqeJl+J0+ROiD5yG+Qe8rS6Sd4X1UthMy5SsJGkBF1ijsLdRQa5obCH/aXgPSsh/xTLUNCWjInmRCdFpJooWtZjFE7oQfoP92i1IA== \ No newline at end of file diff -r 1ff3dd3705b1 -r 99273b7afbff misc/OfficialChallenges/racer_#16.hwmap --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/OfficialChallenges/racer_#16.hwmap Thu Apr 02 21:09:56 2015 +0300 @@ -0,0 +1,1 @@ +AAAAaXicY3RgdGBgWcB2gIGDAcjiTgCy+A6AxVhlgFwgwcHA9ISBgYGRYb8AkLAXYGBmsGeAEKwgLphgB3GBBCPQvD8g82pA5tWAzKsBmVcDAB2iEO4= \ No newline at end of file diff -r 1ff3dd3705b1 -r 99273b7afbff project_files/hwc/rtl/system.c --- a/project_files/hwc/rtl/system.c Mon Feb 16 22:33:15 2015 +0300 +++ b/project_files/hwc/rtl/system.c Thu Apr 02 21:09:56 2015 +0300 @@ -73,6 +73,46 @@ return result; } +void fpcrtl_insert__vars(string255 *src, string255 *dst, SizeInt index) { + int num_insert; + int num_shift; + int num_preshift; + + // nothing to do if empty string is inserted or index invalid + if ((src->len == 0) || (index < 1) || (index > 255)) { + return; + } + + num_insert = src->len; + // number of chars from start of destination string to end of insertion + num_preshift = index - 1 + num_insert; + + // don't overflow on insert + if (num_preshift > 255) { + num_insert = 255 - (index - 1); + num_shift = 0; + } + // shift trailing chars + else { + // number of bytes to be shifted + num_shift = dst->len - (index - 1); + + if (num_shift > 0) { + // don't overflow when shifting + if (num_shift + num_preshift > 255) + num_shift = 255 - num_preshift; + + // time to move some bytes! + memmove(dst->str + num_preshift, dst->str + index - 1, num_shift); + } + } + + // actual byte insertion + memmove(dst->str + index - 1, src->str, num_insert); + // store new length + dst->len = num_shift + num_preshift; +} + void __attribute__((overloadable)) fpcrtl_delete__vars(string255 *s, SizeInt index, SizeInt count) { // number of chars to be move int num_move; @@ -298,7 +338,7 @@ LongInt fpcrtl_random(LongInt l) { // random(0) is undefined in docs but effectively returns 0 in free pascal if (l == 0) { - printf("WARNING: random(0) called!"); + printf("WARNING: random(0) called!\n"); return 0; } return (LongInt) (rand() / (double) RAND_MAX * l); diff -r 1ff3dd3705b1 -r 99273b7afbff project_files/hwc/rtl/system.h --- a/project_files/hwc/rtl/system.h Mon Feb 16 22:33:15 2015 +0300 +++ b/project_files/hwc/rtl/system.h Thu Apr 02 21:09:56 2015 +0300 @@ -26,6 +26,13 @@ astring fpcrtl_copyA(astring s, Integer Index, Integer Count); /* + * Insert a shortstring in another at a specified index + */ +void fpcrtl_insert__vars(string255 *src, string255 *dst, SizeInt index); +#define fpcrtl_insert(src, dst, index) fpcrtl_insert__vars(&(src), &(dst), index); +#define fpcrtl_Insert fpcrtl_insert + +/* * Delete removes Count characters from string S, starting at position Index. * All characters after the deleted characters are shifted Count positions to the left, * and the length of the string is adjusted. @@ -33,6 +40,7 @@ #define fpcrtl_delete(s, index, count) fpcrtl_delete__vars(&(s), index, count) void __attribute__((overloadable)) fpcrtl_delete__vars(string255 *s, SizeInt index, SizeInt count); void __attribute__((overloadable)) fpcrtl_delete__vars(astring *s, SizeInt index, SizeInt count); +#define fpcrtl_Delete fpcrtl_delete string255 fpcrtl_floatToStr(double n); diff -r 1ff3dd3705b1 -r 99273b7afbff qmlFrontend/CMakeLists.txt --- a/qmlFrontend/CMakeLists.txt Mon Feb 16 22:33:15 2015 +0300 +++ b/qmlFrontend/CMakeLists.txt Thu Apr 02 21:09:56 2015 +0300 @@ -9,6 +9,8 @@ find_package(Qt5 COMPONENTS Core Qml Quick Gui) +qt5_add_resources(qresources qmlFrontend.qrc) + add_executable(hedgewars WIN32 main hwengine @@ -16,6 +18,7 @@ themeiconprovider qtquick2applicationviewer/qtquick2applicationviewer flib.h + ${qresources} ) include_directories(${OPENGL_INCLUDE_DIR}) diff -r 1ff3dd3705b1 -r 99273b7afbff qmlFrontend/main.cpp --- a/qmlFrontend/main.cpp Mon Feb 16 22:33:15 2015 +0300 +++ b/qmlFrontend/main.cpp Thu Apr 02 21:09:56 2015 +0300 @@ -13,12 +13,14 @@ HWEngine::exposeToQML(); + Q_INIT_RESOURCE(qmlFrontend); + QtQuick2ApplicationViewer viewer; viewer.engine()->addImageProvider(QLatin1String("preview"), new PreviewImageProvider()); viewer.engine()->addImageProvider(QLatin1String("theme"), new ThemeIconProvider()); - viewer.setMainQmlFile(QStringLiteral("qml/qmlFrontend/main.qml")); + viewer.setSource(QUrl("qrc:/qml/qmlFrontend/main.qml")); viewer.showExpanded(); return app.exec(); diff -r 1ff3dd3705b1 -r 99273b7afbff qmlFrontend/qmlFrontend.qrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlFrontend/qmlFrontend.qrc Thu Apr 02 21:09:56 2015 +0300 @@ -0,0 +1,10 @@ + + + qml/qmlFrontend/First.qml + qml/qmlFrontend/GameConfig.qml + qml/qmlFrontend/HWButton.qml + qml/qmlFrontend/HWComboBox.qml + qml/qmlFrontend/LocalGame.qml + qml/qmlFrontend/main.qml + + diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Maps/ClimbHome/map.lua --- a/share/hedgewars/Data/Maps/ClimbHome/map.lua Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Maps/ClimbHome/map.lua Thu Apr 02 21:09:56 2015 +0300 @@ -43,6 +43,10 @@ local waterAccel = 0 local delayHeight = 32000 local delayTime = 0 +local airMineX = {} +local airMineY = {} +local airMine = {} +local init = true function onParameters() parseParams() @@ -96,6 +100,8 @@ HH[gear] = 1 totalHedgehogs = totalHedgehogs + 1 teams[GetHogTeamName(gear)] = 1 + elseif init and GetGearType(gear) == gtAirMine then + airMine[gear] = 1 end end @@ -144,6 +150,23 @@ end function onNewTurn() + if init then + init = false + for a,i in pairs(airMine) do + x,y = GetGearPosition(a) + airMineX[a] = x + airMineY[a] = y + end + else + for a,i in pairs(airMine) do + local x,y = GetGearPosition(a) + if not x or airMineX[a] ~= x or airMineY[a] ~= y then + DeleteGear(a) + AddGear(airMineX[a],airMineY[a], gtAirMine, gsttmpFlag, 0, 0, 0) + end + end + end + ready = false startTime = GameTime --disable to preserve highest over multiple turns @@ -220,7 +243,7 @@ function onGameTick20() - local x,y; + local x,y if math.random(20) == 1 then AddVisualGear(2012,56,vgtSmoke,0,false) end if CurrentHedgehog == dummyHog and dummySkip ~= 0 and dummySkip < GameTime then ParseCommand("/skip") diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Balanced_Random_Weapon.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Balanced_Random_Weapon.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Balanced_Random_Weapon.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default +* locked diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Capture_the_Flag.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Capture_the_Flag.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Capture_the_Flag.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/DiagonalMaze.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/DiagonalMaze.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/DiagonalMaze.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Gravity.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Gravity.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Gravity.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Highlander.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Highlander.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Highlander.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default +* Highlander diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Highlander.lua --- a/share/hedgewars/Data/Scripts/Multiplayer/Highlander.lua Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Highlander.lua Thu Apr 02 21:09:56 2015 +0300 @@ -110,7 +110,7 @@ [amRope]=true, [amParachute]=true, [amTeleport]=true, [amJetpack]=true, [amInvulnerable]=true, [amLaserSight]=true, [amVampiric]=true, [amLowGravity]=true, [amExtraDamage]=true, [amExtraTime]=true, - [amLandGun]=true, [amSwitch]=true, [amRubber]=true, [amIceGun]=true, + [amLandGun]=true, [amRubber]=true, [amIceGun]=true, } local wepArray = {} diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Mutant.lua --- a/share/hedgewars/Data/Scripts/Multiplayer/Mutant.lua Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Mutant.lua Thu Apr 02 21:09:56 2015 +0300 @@ -21,6 +21,7 @@ HedgewarsScriptLoad("/Scripts/Locale.lua") HedgewarsScriptLoad("/Scripts/Tracker.lua") +HedgewarsScriptLoad("/Scripts/Params.lua") --[[ MUTANT SCRIPT @@ -399,7 +400,7 @@ function setAIHints() for i = 0, #hhs do - if mutant == nil or hhs[i] == mutant or CurrentHedgehog == mutant then + if mutant == nil or hhs[i] == mutant or CurrentHedgehog == mutant or getGearValue(CurrentHedgehog, "Feeder") then SetGearAIHints(hhs[i], aihUsual) else SetGearAIHints(hhs[i], aihDoesntMatter) @@ -607,6 +608,11 @@ end end +function onParameters() + parseParams() + winScore = tonumber(params["winscore"]) or winScore +end + --[[ S T A R R I N G prof - Coding, implementing and evangelism diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/No_Jumping.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/No_Jumping.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/No_Jumping.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Racer.lua --- a/share/hedgewars/Data/Scripts/Multiplayer/Racer.lua Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Racer.lua Thu Apr 02 21:09:56 2015 +0300 @@ -1,772 +1,785 @@ - ------------------------------------------- --- RACER 0.6 --- map-independant racing script --- by mikade ------------------------------------------ - ------------------------------------ ---0.1: took all the code from crazy racer and scrapped most of it ------------------------------------ - --- Removed tumbler system --- Removed extra adds like boosters etc --- Added experimental waypoint placement system --- More user feedback --- Reduced race complexity limit to 5 waypoints --- stop placement at complexity limit reached and end turn --- guys dont keep racing after dying --- invulnerable feasibility --- reverted time keeping method --- reduced feedback display time --- colour-coded addcaptions --- cleaned up code --- support for more players properly added --- tardis fix --- remove airstrikes - --- i think the remainder 0 .456 sec of the tracktime isnt getting reset on newturn - --- update feedback - -------- --- 0.2 -------- - --- allow gameflags --- extend time to 90s --- remove other air-attack based weps --- turn off water rise for sd - -------- --- 0.3 -------- - --- prevent WP being placed in land --- prevent waypoints being placed outside border - -------- --- 0.4 -------- - --- update user feedback --- add more sounds - -------- --- 0.5 -------- - --- fix ghost disappearing if hog falls in water or somehow dies --- lengthen ghost tracking interval to improve performance on slower machines --- increase waypoint limit to 8 --- allow for persistent showmission information - -------- --- 0.6 -------- - --- remove hogs from racing area as per request - -------- --- 0.7 -------- - --- switch to first available weapon if starting race with no weapon selected - ------------------------------ --- SCRIPT BEGINS ------------------------------ - -HedgewarsScriptLoad("/Scripts/Locale.lua") -HedgewarsScriptLoad("/Scripts/OfficialChallenges.lua") - ------------------- --- Got Variables? ------------------- - -local fMod = 1000000 -- 1 -local roundLimit = 3 -local roundNumber = 0 -local firstClan = 10 - -local fastX = {} -local fastY = {} -local fastCount = 0 -local fastIndex = 0 -local fastColour - -local currX = {} -local currY = {} -local currCount = 0 - -local specialPointsX = {} -local specialPointsY = {} -local specialPointsCount = 0 - --------------------------- --- hog and team tracking variales --------------------------- - -local numhhs = 0 -- store number of hedgehogs -local hhs = {} -- store hedgehog gears - -local numTeams -- store the number of teams in the game -local teamNameArr = {} -- store the list of teams -local teamClan = {} -local teamSize = {} -- store how many hogs per team -local teamIndex = {} -- at what point in the hhs{} does each team begin - -local teamComment = {} -local teamScore = {} - -------- --- racer vars --------- - -local cGear = nil - -local bestClan = nil -local bestTime = nil - -local gameBegun = false -local gameOver = false -local racerActive = false -local trackTime = 0 - -local wpCirc = {} -local wpX = {} -local wpY = {} -local wpCol = {} -local wpActive = {} -local wpRad = 450 --75 -local wpCount = 0 -local wpLimit = 8 - -local usedWeapons = {} - -local roundN -local lastRound -local RoundHasChanged - -------------------- --- general methods -------------------- - -function RebuildTeamInfo() - - - -- make a list of individual team names - for i = 0, (TeamsCount-1) do - teamNameArr[i] = " " -- = i - teamSize[i] = 0 - teamIndex[i] = 0 - teamScore[i] = 100000 - end - numTeams = 0 - - for i = 0, (numhhs-1) do - - z = 0 - unfinished = true - while(unfinished == true) do - - newTeam = true - tempHogTeamName = GetHogTeamName(hhs[i]) -- this is the new name - - if tempHogTeamName == teamNameArr[z] then - newTeam = false - unfinished = false - end - - z = z + 1 - - if z == TeamsCount then - unfinished = false - if newTeam == true then - teamNameArr[numTeams] = tempHogTeamName - numTeams = numTeams + 1 - end - end - - end - - end - - -- find out how many hogs per team, and the index of the first hog in hhs - for i = 0, (numTeams-1) do - for z = 0, (numhhs-1) do - if GetHogTeamName(hhs[z]) == teamNameArr[i] then - teamClan[i] = GetHogClan(hhs[z]) - if teamSize[i] == 0 then - teamIndex[i] = z -- should give starting index - end - teamSize[i] = teamSize[i] + 1 - --add a pointer so this hog appears at i in hhs - end - end - - end - -end - - ------------------ --- RACER METHODS ------------------ - -function CheckWaypoints() - - trackFinished = true - - for i = 0, (wpCount-1) do - - g1X, g1Y = GetGearPosition(CurrentHedgehog) - g2X, g2Y = wpX[i], wpY[i] - - g1X = g1X - g2X - g1Y = g1Y - g2Y - dist = (g1X*g1X) + (g1Y*g1Y) - - --if i == 0 then - -- AddCaption(dist .. "/" .. (wpRad*wpRad) ) - --end - - NR = (48/100*wpRad)/2 - - if dist < (NR*NR) then - --if dist < (wpRad*wpRad) then - --AddCaption("howdy") - wpActive[i] = true - wpCol[i] = GetClanColor(GetHogClan(CurrentHedgehog)) -- new --GetClanColor(1) - SetVisualGearValues(wpCirc[i], wpX[i], wpY[i], 20, 100, 1, 10, 0, wpRad, 5, wpCol[i]) - - wpRem = 0 - for k = 0, (wpCount-1) do - if wpActive[k] == false then - wpRem = wpRem + 1 - end - end - - AddCaption(loc("Way-Points Remaining") .. ": " .. wpRem,0xffba00ff,capgrpAmmoinfo) - - end - - if wpActive[i] == false then - trackFinished = false - end - - end - - return(trackFinished) - -end - -function AdjustScores() - - if bestTime == nil then - bestTime = 100000 - bestClan = 10 - bestTimeComment = "N/A" - end - - newScore = false - - -- update this clan's time if the new track is better - for i = 0, (numTeams-1) do - if teamClan[i] == GetHogClan(CurrentHedgehog) then - if trackTime < teamScore[i] then - teamScore[i] = trackTime - newScore = true - else - newScore = false - end - end - end - - --bestTime = 100000 - --bestClan = 10 - - -- find the best time out of those so far - for i = 0, (numTeams-1) do - if teamScore[i] < bestTime then - bestTime = teamScore[i] - bestClan = teamClan[i] - end - end - - if bestTime ~= 100000 then - bestTimeComment = (bestTime/1000) ..loc("s") - end - - if newScore == true then - if trackTime == bestTime then -- best time of the race - ShowMission(loc("RACER"), - loc("TRACK COMPLETED"), - loc("NEW RACE RECORD: ") .. (trackTime/1000) ..loc("s") .. "|" .. - loc("WINNING TIME: ") .. bestTimeComment, 0, 4000) - PlaySound(sndHomerun) - else -- best time for the clan - ShowMission(loc("RACER"), - loc("TRACK COMPLETED"), - loc("NEW CLAN RECORD: ") .. (trackTime/1000) ..loc("s") .. "|" .. - loc("WINNING TIME: ") .. bestTimeComment, 4, 4000) - end - else -- not any kind of new score - ShowMission(loc("RACER"), - loc("TRACK COMPLETED"), - loc("TIME: ") .. (trackTime/1000) ..loc("s") .. "|" .. - loc("WINNING TIME: ") .. bestTimeComment, -amSkip, 4000) - PlaySound(sndHellish) - end - - - -------- - --new - -------- - - if bestTime == trackTime then - --AddCaption("wooooooooooooooooooooooooooooo") - - fastColour = GetClanColor(GetHogClan(CurrentHedgehog)) - - for i = 0, (currCount-1) do - fastX[i] = currX[i] - fastY[i] = currY[i] - end - - fastCount = currCount - fastIndex = 0 - - --currCount = 0 -- is this needed? - - else - currCount = 0 - fastIndex = 0 - end - - -end - -function onNewRound() - - roundNumber = roundNumber + 1 - - totalComment = "" - for i = 0, (TeamsCount-1) do - if teamNameArr[i] ~= " " then -- teamScore[teamClan[i]] - teamComment[i] = teamNameArr[i] .. ": " .. (teamScore[i]/1000) .. loc("s|") - totalComment = totalComment .. teamComment[i] - elseif teamNameArr[i] == " " then - teamComment[i] = "|" - end - end - - ShowMission( loc("RACER"), - loc("STATUS UPDATE"), - loc("Rounds Complete: ") .. roundNumber .. "/" .. roundLimit .. "|" .. " " .. "|" .. - loc("Best Team Times: ") .. "|" .. totalComment, 0, 4000) - - -- end game if its at round limit - if roundNumber >= roundLimit then - for i = 0, (numhhs-1) do - if GetHogClan(hhs[i]) ~= bestClan then - SetEffect(hhs[i], heResurrectable, 0) - SetHealth(hhs[i],0) - end - end - gameOver = true - TurnTimeLeft = 1 - end - -end - -function CheckForNewRound() - - ------------- - ------ new - ------------- - - --[[turnN = turnN + 1 - if gameBegun == false then - if turnN == 2 then - for i = 0, (numhhs-1) do - if hhs[i] ~= nil then - SetEffect(hhs[i], heResurrectable, 0) - SetHealth(hhs[i],0) - end - end - gameOver = true - TurnTimeLeft = 1 - end - else - - - end]] - - --[[if roundBegun == true then - - if RoundHasChanged == true then - roundN = roundN + 1 - RoundHasChanged = false - onNewRound() - end - - if lastRound ~= TotalRounds then -- new round, but not really - - if RoundHasChanged == false then - RoundHasChanged = true - end - - end - - AddCaption("RoundN:" .. roundN .. "; " .. "TR: " .. TotalRounds) - - lastRound = TotalRounds - - end]] - - ------------ - ----- old - ------------ - - if GetHogClan(CurrentHedgehog) == firstClan then - onNewRound() - end - -end - -function DisableTumbler() - currCount = 0 - fastIndex = 0 - TurnTimeLeft = 0 - racerActive = false -- newadd -end - -function HandleGhost() - - -- get the current xy of the racer at this point - currX[currCount] = GetX(CurrentHedgehog) - currY[currCount] = GetY(CurrentHedgehog) - currCount = currCount + 1 - - -- draw a ping of smoke where the fastest player was at this point - if (fastCount ~= 0) and (fastIndex < fastCount) then - - fastIndex = fastIndex + 1 - - tempE = AddVisualGear(fastX[fastIndex], fastY[fastIndex], vgtSmoke, 0, false) - g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE) - SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, g8, g9, fastColour ) - - --AddCaption("fC: " .. fastIndex .. " / " .. fastCount) - - else - - --AddCaption("excep fC: " .. fastIndex .. " / " .. fastCount) - - end - - - -end - -function TryRepositionHogs() - - if MapHasBorder() == true then - - for i = 0, (numhhs-1) do - if hhs[i] ~= nil then - SetGearPosition(hhs[i],GetX(hhs[i]), TopY-10) - end - end - - end - -end - ----------------------------------- --- GAME METHODS / EVENT HANDLERS ----------------------------------- - -function onGameInit() - EnableGameFlags(gfInfAttack, gfInvulnerable) - CaseFreq = 0 - TurnTime = 90000 - WaterRise = 0 -end - - -function onGameStart() - - roundN = 0 - lastRound = TotalRounds - RoundHasChanged = false -- true - - for i = 0, (specialPointsCount-1) do - PlaceWayPoint(specialPointsX[i], specialPointsY[i]) - end - - RebuildTeamInfo() - - ShowMission ( - loc("RACER"), - loc("a Hedgewars mini-game"), - - loc("Build a track and race.") .. "|" .. - loc("Round Limit:") .. " " .. roundLimit .. "|" .. - - "", 4, 4000 - ) - - TryRepositionHogs() - -end - -function PlaceWayPoint(x,y) - if not racerActive then - if wpCount == 0 or wpX[wpCount - 1] ~= x or wpY[wpCount - 1] ~= y then - - wpX[wpCount] = x - wpY[wpCount] = y - wpCol[wpCount] = 0xffffffff - wpCirc[wpCount] = AddVisualGear(wpX[wpCount],wpY[wpCount],vgtCircle,0,true) - - SetVisualGearValues(wpCirc[wpCount], wpX[wpCount], wpY[wpCount], 20, 100, 1, 10, 0, wpRad, 5, wpCol[wpCount]) - - wpCount = wpCount + 1 - - AddCaption(loc("Waypoint placed.") .. " " .. loc("Available points remaining: ") .. (wpLimit-wpCount)) - end - end -end - -function onSpecialPoint(x,y,flag) - specialPointsX[specialPointsCount] = x - specialPointsY[specialPointsCount] = y - specialPointsCount = specialPointsCount + 1 -end - -function onNewTurn() - - CheckForNewRound() - TryRepositionHogs() - - racerActive = false - - trackTime = 0 - - currCount = 0 -- hopefully this solves problem - AddAmmo(CurrentHedgehog, amAirAttack, 0) - gTimer = 0 - - -- Set the waypoints to unactive on new round - for i = 0,(wpCount-1) do - wpActive[i] = false - wpCol[i] = 0xffffffff - SetVisualGearValues(wpCirc[i], wpX[i], wpY[i], 20, 100, 1, 10, 0, wpRad, 5, wpCol[i]) - end - - -- Handle Starting Stage of Game - if (gameOver == false) and (gameBegun == false) then - if wpCount >= 3 then - gameBegun = true - roundNumber = 0 - firstClan = GetHogClan(CurrentHedgehog) - ShowMission(loc("RACER"), - loc("GAME BEGUN!!!"), - loc("Complete the track as fast as you can!"), 2, 4000) - else - ShowMission(loc("RACER"), - loc("NOT ENOUGH WAYPOINTS"), - loc("Place more waypoints using the 'Air Attack' weapon."), 2, 4000) - AddAmmo(CurrentHedgehog, amAirAttack, 4000) - SetWeapon(amAirAttack) - end - end - - if gameOver == true then - gameBegun = false - racerActive = false -- newadd - end - - AddAmmo(CurrentHedgehog, amTardis, 0) - AddAmmo(CurrentHedgehog, amDrillStrike, 0) - AddAmmo(CurrentHedgehog, amMineStrike, 0) - AddAmmo(CurrentHedgehog, amNapalm, 0) - AddAmmo(CurrentHedgehog, amPiano, 0) - -end - -function onGameTick20() - - -- airstrike detected, convert this into a potential waypoint spot - if cGear ~= nil then - x,y = GetGearPosition(cGear) - if x > -9000 then - x,y = GetGearTarget(cGear) - - - if TestRectForObstacle(x-20, y-20, x+20, y+20, true) then - AddCaption(loc("Please place the way-point in the open, within the map boundaries.")) - PlaySound(sndDenied) - elseif (y > WaterLine-50) then - AddCaption(loc("Please place the way-point further from the waterline.")) - PlaySound(sndDenied) - else - PlaceWayPoint(x, y) - if wpCount == wpLimit then - AddCaption(loc("Race complexity limit reached.")) - DisableTumbler() - end - end - else - DeleteGear(cGear) - end - SetGearPosition(cGear, -10000, 0) - end - - - -- start the player tumbling with a boom once their turn has actually begun - if racerActive == false then - - if (TurnTimeLeft > 0) and (TurnTimeLeft ~= TurnTime) then - - -- if the gamehas started put the player in the middle of the first - --waypoint that was placed - if gameBegun == true then - AddCaption(loc("Good to go!")) - racerActive = true - trackTime = 0 - - SetGearPosition(CurrentHedgehog, wpX[0], wpY[0]) - AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtGrenade, 0, 0, 0, 1) - FollowGear(CurrentHedgehog) - - HideMission() - - -- don't start empty-handed - if (GetCurAmmoType() == amNothing) then - SetNextWeapon() - end - else - -- still in placement mode - end - - end - end - - - -- has the player started his tumbling spree? - if (CurrentHedgehog ~= nil) then - - --airstrike conversion used to be here - - -- if the RACE has started, show tracktimes and keep tabs on waypoints - if (racerActive == true) and (gameBegun == true) then - - --ghost - if GameTime%40 == 0 then - HandleGhost() - end - - trackTime = trackTime + 20 - - if GameTime%100 == 0 then - - if trackTime%1000 == 0 then - AddCaption((trackTime/1000)..'.0',GetClanColor(GetHogClan(CurrentHedgehog)),capgrpMessage2) - else - AddCaption(trackTime/1000,GetClanColor(GetHogClan(CurrentHedgehog)),capgrpMessage2) - end - - if (CheckWaypoints() == true) then - AdjustScores() - DisableTumbler() - end - - end - - end - - -- if the player has expended his tunbling time, stop him tumbling - if TurnTimeLeft <= 20 then - DisableTumbler() - end - - end - -end - -function onGearResurrect(gear) - - AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false) - - if gear == CurrentHedgehog then - DisableTumbler() - end - -end - -function onGearAdd(gear) - - if GetGearType(gear) == gtHedgehog then - hhs[numhhs] = gear - numhhs = numhhs + 1 - SetEffect(gear, heResurrectable, 1) - end - - if GetGearType(gear) == gtAirAttack then - cGear = gear - end - -end - -function onGearDelete(gear) - - if GetGearType(gear) == gtAirAttack then - cGear = nil - end - -end - -function onAttack() - at = GetCurAmmoType() - - usedWeapons[at] = 0 -end - -function onAchievementsDeclaration() - usedWeapons[amSkip] = nil - - usedRope = usedWeapons[amRope] ~= nil - usedPortal = usedWeapons[amPortalGun] ~= nil - usedSaucer = usedWeapons[amJetpack] ~= nil - - usedWeapons[amRope] = nil - usedWeapons[amPortalGun] = nil - usedWeapons[amJetpack] = nil - - usedOther = next(usedWeapons) ~= nil - - if usedOther then -- smth besides skip, rope, portal or saucer used - raceType = "unknown race" - elseif usedRope and not usedPortal and not usedSaucer then - raceType = "rope race" - elseif not usedRope and usedPortal and not usedSaucer then - raceType = "portal race" - elseif not usedRope and not usedPortal and usedSaucer then - raceType = "saucer race" - elseif (usedRope or usedPortal or usedSaucer or usedOther) == false then -- no weapons used at all? - raceType = "no tools race" - else -- at least two of rope, portal and saucer used - raceType = "mixed race" - end - - map = detectMap() - - for i = 0, (numTeams-1) do - if teamScore[i] < 100000 then - DeclareAchievement(raceType, teamNameArr[i], map, teamScore[i]) - end - end -end + +------------------------------------------ +-- RACER 0.6 +-- map-independant racing script +-- by mikade +----------------------------------------- + +----------------------------------- +--0.1: took all the code from crazy racer and scrapped most of it +----------------------------------- + +-- Removed tumbler system +-- Removed extra adds like boosters etc +-- Added experimental waypoint placement system +-- More user feedback +-- Reduced race complexity limit to 5 waypoints +-- stop placement at complexity limit reached and end turn +-- guys dont keep racing after dying +-- invulnerable feasibility +-- reverted time keeping method +-- reduced feedback display time +-- colour-coded addcaptions +-- cleaned up code +-- support for more players properly added +-- tardis fix +-- remove airstrikes + +-- i think the remainder 0 .456 sec of the tracktime isnt getting reset on newturn + +-- update feedback + +------- +-- 0.2 +------- + +-- allow gameflags +-- extend time to 90s +-- remove other air-attack based weps +-- turn off water rise for sd + +------- +-- 0.3 +------- + +-- prevent WP being placed in land +-- prevent waypoints being placed outside border + +------- +-- 0.4 +------- + +-- update user feedback +-- add more sounds + +------- +-- 0.5 +------- + +-- fix ghost disappearing if hog falls in water or somehow dies +-- lengthen ghost tracking interval to improve performance on slower machines +-- increase waypoint limit to 8 +-- allow for persistent showmission information + +------- +-- 0.6 +------- + +-- remove hogs from racing area as per request + +------- +-- 0.7 +------- + +-- switch to first available weapon if starting race with no weapon selected + +----------------------------- +-- SCRIPT BEGINS +----------------------------- + +HedgewarsScriptLoad("/Scripts/Locale.lua") +HedgewarsScriptLoad("/Scripts/OfficialChallenges.lua") +HedgewarsScriptLoad("/Scripts/Params.lua") + +------------------ +-- Got Variables? +------------------ + +local fMod = 1000000 -- 1 +local roundLimit = 3 +local roundNumber = 0 +local firstClan = 10 + +local fastX = {} +local fastY = {} +local fastCount = 0 +local fastIndex = 0 +local fastColour + +local currX = {} +local currY = {} +local currCount = 0 + +local specialPointsX = {} +local specialPointsY = {} +local specialPointsCount = 0 + +local TeamRope = false + +-------------------------- +-- hog and team tracking variales +-------------------------- + +local numhhs = 0 -- store number of hedgehogs +local hhs = {} -- store hedgehog gears + +local numTeams -- store the number of teams in the game +local teamNameArr = {} -- store the list of teams +local teamClan = {} +local teamSize = {} -- store how many hogs per team +local teamIndex = {} -- at what point in the hhs{} does each team begin + +local teamComment = {} +local teamScore = {} + +------- +-- racer vars +-------- + +local cGear = nil + +local bestClan = nil +local bestTime = nil + +local gameBegun = false +local gameOver = false +local racerActive = false +local trackTime = 0 + +local wpCirc = {} +local wpX = {} +local wpY = {} +local wpCol = {} +local wpActive = {} +local wpRad = 450 --75 +local wpCount = 0 +local wpLimit = 8 + +local usedWeapons = {} + +local roundN +local lastRound +local RoundHasChanged + +------------------- +-- general methods +------------------- + +function onParameters() + parseParams() + if params["teamrope"] ~= nil then + TeamRope = true + end +end + +function RebuildTeamInfo() + + + -- make a list of individual team names + for i = 0, (TeamsCount-1) do + teamNameArr[i] = " " -- = i + teamSize[i] = 0 + teamIndex[i] = 0 + teamScore[i] = 100000 + end + numTeams = 0 + + for i = 0, (numhhs-1) do + + z = 0 + unfinished = true + while(unfinished == true) do + + newTeam = true + tempHogTeamName = GetHogTeamName(hhs[i]) -- this is the new name + + if tempHogTeamName == teamNameArr[z] then + newTeam = false + unfinished = false + end + + z = z + 1 + + if z == TeamsCount then + unfinished = false + if newTeam == true then + teamNameArr[numTeams] = tempHogTeamName + numTeams = numTeams + 1 + end + end + + end + + end + + -- find out how many hogs per team, and the index of the first hog in hhs + for i = 0, (numTeams-1) do + for z = 0, (numhhs-1) do + if GetHogTeamName(hhs[z]) == teamNameArr[i] then + teamClan[i] = GetHogClan(hhs[z]) + if teamSize[i] == 0 then + teamIndex[i] = z -- should give starting index + end + teamSize[i] = teamSize[i] + 1 + --add a pointer so this hog appears at i in hhs + end + end + + end + +end + + +----------------- +-- RACER METHODS +----------------- + +function CheckWaypoints() + + trackFinished = true + + for i = 0, (wpCount-1) do + + g1X, g1Y = GetGearPosition(CurrentHedgehog) + g2X, g2Y = wpX[i], wpY[i] + + g1X = g1X - g2X + g1Y = g1Y - g2Y + dist = (g1X*g1X) + (g1Y*g1Y) + + --if i == 0 then + -- AddCaption(dist .. "/" .. (wpRad*wpRad) ) + --end + + NR = (48/100*wpRad)/2 + + if dist < (NR*NR) then + --if dist < (wpRad*wpRad) then + --AddCaption("howdy") + wpActive[i] = true + wpCol[i] = GetClanColor(GetHogClan(CurrentHedgehog)) -- new --GetClanColor(1) + SetVisualGearValues(wpCirc[i], wpX[i], wpY[i], 20, 100, 1, 10, 0, wpRad, 5, wpCol[i]) + + wpRem = 0 + for k = 0, (wpCount-1) do + if wpActive[k] == false then + wpRem = wpRem + 1 + end + end + + AddCaption(loc("Way-Points Remaining") .. ": " .. wpRem,0xffba00ff,capgrpAmmoinfo) + + end + + if wpActive[i] == false then + trackFinished = false + end + + end + + return(trackFinished) + +end + +function AdjustScores() + + if bestTime == nil then + bestTime = 100000 + bestClan = 10 + bestTimeComment = "N/A" + end + + newScore = false + + -- update this clan's time if the new track is better + for i = 0, (numTeams-1) do + if teamClan[i] == GetHogClan(CurrentHedgehog) then + if trackTime < teamScore[i] then + teamScore[i] = trackTime + newScore = true + else + newScore = false + end + end + end + + --bestTime = 100000 + --bestClan = 10 + + -- find the best time out of those so far + for i = 0, (numTeams-1) do + if teamScore[i] < bestTime then + bestTime = teamScore[i] + bestClan = teamClan[i] + end + end + + if bestTime ~= 100000 then + bestTimeComment = (bestTime/1000) ..loc("s") + end + + if newScore == true then + if trackTime == bestTime then -- best time of the race + ShowMission(loc("RACER"), + loc("TRACK COMPLETED"), + loc("NEW RACE RECORD: ") .. (trackTime/1000) ..loc("s") .. "|" .. + loc("WINNING TIME: ") .. bestTimeComment, 0, 4000) + PlaySound(sndHomerun) + else -- best time for the clan + ShowMission(loc("RACER"), + loc("TRACK COMPLETED"), + loc("NEW CLAN RECORD: ") .. (trackTime/1000) ..loc("s") .. "|" .. + loc("WINNING TIME: ") .. bestTimeComment, 4, 4000) + end + else -- not any kind of new score + ShowMission(loc("RACER"), + loc("TRACK COMPLETED"), + loc("TIME: ") .. (trackTime/1000) ..loc("s") .. "|" .. + loc("WINNING TIME: ") .. bestTimeComment, -amSkip, 4000) + PlaySound(sndHellish) + end + + + -------- + --new + -------- + + if bestTime == trackTime then + --AddCaption("wooooooooooooooooooooooooooooo") + + fastColour = GetClanColor(GetHogClan(CurrentHedgehog)) + + for i = 0, (currCount-1) do + fastX[i] = currX[i] + fastY[i] = currY[i] + end + + fastCount = currCount + fastIndex = 0 + + --currCount = 0 -- is this needed? + + else + currCount = 0 + fastIndex = 0 + end + + +end + +function onNewRound() + + roundNumber = roundNumber + 1 + + totalComment = "" + for i = 0, (TeamsCount-1) do + if teamNameArr[i] ~= " " then -- teamScore[teamClan[i]] + teamComment[i] = teamNameArr[i] .. ": " .. (teamScore[i]/1000) .. loc("s|") + totalComment = totalComment .. teamComment[i] + elseif teamNameArr[i] == " " then + teamComment[i] = "|" + end + end + + ShowMission( loc("RACER"), + loc("STATUS UPDATE"), + loc("Rounds Complete: ") .. roundNumber .. "/" .. roundLimit .. "|" .. " " .. "|" .. + loc("Best Team Times: ") .. "|" .. totalComment, 0, 4000) + + -- end game if its at round limit + if roundNumber >= roundLimit then + for i = 0, (numhhs-1) do + if GetHogClan(hhs[i]) ~= bestClan then + SetEffect(hhs[i], heResurrectable, 0) + SetHealth(hhs[i],0) + end + end + gameOver = true + TurnTimeLeft = 1 + end + +end + +function CheckForNewRound() + + ------------- + ------ new + ------------- + + --[[turnN = turnN + 1 + if gameBegun == false then + if turnN == 2 then + for i = 0, (numhhs-1) do + if hhs[i] ~= nil then + SetEffect(hhs[i], heResurrectable, 0) + SetHealth(hhs[i],0) + end + end + gameOver = true + TurnTimeLeft = 1 + end + else + + + end]] + + --[[if roundBegun == true then + + if RoundHasChanged == true then + roundN = roundN + 1 + RoundHasChanged = false + onNewRound() + end + + if lastRound ~= TotalRounds then -- new round, but not really + + if RoundHasChanged == false then + RoundHasChanged = true + end + + end + + AddCaption("RoundN:" .. roundN .. "; " .. "TR: " .. TotalRounds) + + lastRound = TotalRounds + + end]] + + ------------ + ----- old + ------------ + + if GetHogClan(CurrentHedgehog) == firstClan then + onNewRound() + end + +end + +function DisableTumbler() + currCount = 0 + fastIndex = 0 + TurnTimeLeft = 0 + racerActive = false -- newadd +end + +function HandleGhost() + + -- get the current xy of the racer at this point + currX[currCount] = GetX(CurrentHedgehog) + currY[currCount] = GetY(CurrentHedgehog) + currCount = currCount + 1 + + -- draw a ping of smoke where the fastest player was at this point + if (fastCount ~= 0) and (fastIndex < fastCount) then + + fastIndex = fastIndex + 1 + + tempE = AddVisualGear(fastX[fastIndex], fastY[fastIndex], vgtSmoke, 0, false) + g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE) + SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, g8, g9, fastColour ) + + --AddCaption("fC: " .. fastIndex .. " / " .. fastCount) + + else + + --AddCaption("excep fC: " .. fastIndex .. " / " .. fastCount) + + end + + + +end + +function TryRepositionHogs() + + if MapHasBorder() == true then + + for i = 0, (numhhs-1) do + if hhs[i] ~= nil then + SetGearPosition(hhs[i],GetX(hhs[i]), TopY-10) + end + end + + end + +end + +---------------------------------- +-- GAME METHODS / EVENT HANDLERS +---------------------------------- + +function onGameInit() + EnableGameFlags(gfInfAttack, gfInvulnerable) + CaseFreq = 0 + TurnTime = 90000 + WaterRise = 0 +end + + +function onGameStart() + + roundN = 0 + lastRound = TotalRounds + RoundHasChanged = false -- true + + for i = 0, (specialPointsCount-1) do + PlaceWayPoint(specialPointsX[i], specialPointsY[i]) + end + + RebuildTeamInfo() + + ShowMission ( + loc("RACER"), + loc("a Hedgewars mini-game"), + + loc("Build a track and race.") .. "|" .. + loc("Round Limit:") .. " " .. roundLimit .. "|" .. + + "", 4, 4000 + ) + + TryRepositionHogs() + +end + +function PlaceWayPoint(x,y) + if not racerActive then + if wpCount == 0 or wpX[wpCount - 1] ~= x or wpY[wpCount - 1] ~= y then + + wpX[wpCount] = x + wpY[wpCount] = y + wpCol[wpCount] = 0xffffffff + wpCirc[wpCount] = AddVisualGear(wpX[wpCount],wpY[wpCount],vgtCircle,0,true) + + SetVisualGearValues(wpCirc[wpCount], wpX[wpCount], wpY[wpCount], 20, 100, 1, 10, 0, wpRad, 5, wpCol[wpCount]) + + wpCount = wpCount + 1 + + AddCaption(loc("Waypoint placed.") .. " " .. loc("Available points remaining: ") .. (wpLimit-wpCount)) + end + end +end + +function onSpecialPoint(x,y,flag) + specialPointsX[specialPointsCount] = x + specialPointsY[specialPointsCount] = y + specialPointsCount = specialPointsCount + 1 +end + +function onNewTurn() + + CheckForNewRound() + TryRepositionHogs() + + racerActive = false + + trackTime = 0 + + currCount = 0 -- hopefully this solves problem + AddAmmo(CurrentHedgehog, amAirAttack, 0) + gTimer = 0 + + -- Set the waypoints to unactive on new round + for i = 0,(wpCount-1) do + wpActive[i] = false + wpCol[i] = 0xffffffff + SetVisualGearValues(wpCirc[i], wpX[i], wpY[i], 20, 100, 1, 10, 0, wpRad, 5, wpCol[i]) + end + + -- Handle Starting Stage of Game + if (gameOver == false) and (gameBegun == false) then + if wpCount >= 3 then + gameBegun = true + roundNumber = 0 + firstClan = GetHogClan(CurrentHedgehog) + ShowMission(loc("RACER"), + loc("GAME BEGUN!!!"), + loc("Complete the track as fast as you can!"), 2, 4000) + else + ShowMission(loc("RACER"), + loc("NOT ENOUGH WAYPOINTS"), + loc("Place more waypoints using the 'Air Attack' weapon."), 2, 4000) + AddAmmo(CurrentHedgehog, amAirAttack, 4000) + SetWeapon(amAirAttack) + end + end + + if gameOver == true then + gameBegun = false + racerActive = false -- newadd + end + + AddAmmo(CurrentHedgehog, amTardis, 0) + AddAmmo(CurrentHedgehog, amDrillStrike, 0) + AddAmmo(CurrentHedgehog, amMineStrike, 0) + AddAmmo(CurrentHedgehog, amNapalm, 0) + AddAmmo(CurrentHedgehog, amPiano, 0) + +end + +function onGameTick20() + + -- airstrike detected, convert this into a potential waypoint spot + if cGear ~= nil then + x,y = GetGearPosition(cGear) + if x > -9000 then + x,y = GetGearTarget(cGear) + + + if TestRectForObstacle(x-20, y-20, x+20, y+20, true) then + AddCaption(loc("Please place the way-point in the open, within the map boundaries.")) + PlaySound(sndDenied) + elseif (y > WaterLine-50) then + AddCaption(loc("Please place the way-point further from the waterline.")) + PlaySound(sndDenied) + else + PlaceWayPoint(x, y) + if wpCount == wpLimit then + AddCaption(loc("Race complexity limit reached.")) + DisableTumbler() + end + end + else + DeleteGear(cGear) + end + SetGearPosition(cGear, -10000, 0) + end + + + -- start the player tumbling with a boom once their turn has actually begun + if racerActive == false then + + if (TurnTimeLeft > 0) and (TurnTimeLeft ~= TurnTime) then + + -- if the gamehas started put the player in the middle of the first + --waypoint that was placed + if gameBegun == true then + AddCaption(loc("Good to go!")) + racerActive = true + trackTime = 0 + + SetGearPosition(CurrentHedgehog, wpX[0], wpY[0]) + AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtGrenade, 0, 0, 0, 1) + FollowGear(CurrentHedgehog) + + HideMission() + + -- don't start empty-handed + if (GetCurAmmoType() == amNothing) then + SetNextWeapon() + end + else + -- still in placement mode + end + + end + end + + + -- has the player started his tumbling spree? + if (CurrentHedgehog ~= nil) then + + --airstrike conversion used to be here + + -- if the RACE has started, show tracktimes and keep tabs on waypoints + if (racerActive == true) and (gameBegun == true) then + + --ghost + if GameTime%40 == 0 then + HandleGhost() + end + + trackTime = trackTime + 20 + + if GameTime%100 == 0 then + + if trackTime%1000 == 0 then + AddCaption((trackTime/1000)..'.0',GetClanColor(GetHogClan(CurrentHedgehog)),capgrpMessage2) + else + AddCaption(trackTime/1000,GetClanColor(GetHogClan(CurrentHedgehog)),capgrpMessage2) + end + + if (CheckWaypoints() == true) then + AdjustScores() + DisableTumbler() + end + + end + + end + + -- if the player has expended his tunbling time, stop him tumbling + if TurnTimeLeft <= 20 then + DisableTumbler() + end + + end + +end + +function onGearResurrect(gear) + + AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false) + + if gear == CurrentHedgehog then + DisableTumbler() + end + +end + +function onGearAdd(gear) + + if GetGearType(gear) == gtHedgehog then + hhs[numhhs] = gear + numhhs = numhhs + 1 + SetEffect(gear, heResurrectable, 1) + elseif GetGearType(gear) == gtAirAttack then + cGear = gear + elseif GetGearType(gear) == gtRope and TeamRope then + SetTag(gear,1) + SetGearValues(gear,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,GetClanColor(GetHogClan(CurrentHedgehog))) + elseif GetGearType(gear) == gtAirMine then + DeleteGear(gear) + end +end + +function onGearDelete(gear) + + if GetGearType(gear) == gtAirAttack then + cGear = nil + end + +end + +function onAttack() + at = GetCurAmmoType() + + usedWeapons[at] = 0 +end + +function onAchievementsDeclaration() + usedWeapons[amSkip] = nil + + usedRope = usedWeapons[amRope] ~= nil + usedPortal = usedWeapons[amPortalGun] ~= nil + usedSaucer = usedWeapons[amJetpack] ~= nil + + usedWeapons[amNothing] = nil + usedWeapons[amRope] = nil + usedWeapons[amPortalGun] = nil + usedWeapons[amJetpack] = nil + + usedOther = next(usedWeapons) ~= nil + + if usedOther then -- smth besides nothing, skip, rope, portal or saucer used + raceType = "unknown race" + elseif usedRope and not usedPortal and not usedSaucer then + raceType = "rope race" + elseif not usedRope and usedPortal and not usedSaucer then + raceType = "portal race" + elseif not usedRope and not usedPortal and usedSaucer then + raceType = "saucer race" + elseif (usedRope or usedPortal or usedSaucer or usedOther) == false then -- no weapons used at all? + raceType = "no tools race" + else -- at least two of rope, portal and saucer used + raceType = "mixed race" + end + + map = detectMap() + + for i = 0, (numTeams-1) do + if teamScore[i] < 100000 then + DeclareAchievement(raceType, teamNameArr[i], map, teamScore[i]) + end + end +end diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Random_Weapon.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Random_Weapon.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Random_Weapon.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default +* locked diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/ShoppaMap.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/ShoppaMap.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/ShoppaMap.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Space_Invasion.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Space_Invasion.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Space_Invasion.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/The_Specialists.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/The_Specialists.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/The_Specialists.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +locked diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Tumbler.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Tumbler.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Tumbler.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/Multiplayer/Tunnels.cfg --- a/share/hedgewars/Data/Scripts/Multiplayer/Tunnels.cfg Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/Multiplayer/Tunnels.cfg Thu Apr 02 21:09:56 2015 +0300 @@ -1,2 +1,2 @@ -Default -Default +* +* diff -r 1ff3dd3705b1 -r 99273b7afbff share/hedgewars/Data/Scripts/OfficialChallenges.lua --- a/share/hedgewars/Data/Scripts/OfficialChallenges.lua Mon Feb 16 22:33:15 2015 +0300 +++ b/share/hedgewars/Data/Scripts/OfficialChallenges.lua Thu Apr 02 21:09:56 2015 +0300 @@ -20,6 +20,12 @@ return("Racer Challenge #11") elseif LandDigest == "M706743197Scripts/Multiplayer/Racer.lua" then return("Racer Challenge #12") + elseif LandDigest == "M157242054Scripts/Multiplayer/Racer.lua" then + return("Racer Challenge #13") + elseif LandDigest == "M-1585582638Scripts/Multiplayer/Racer.lua" then + return("Racer Challenge #14") + elseif LandDigest == "M-528106034Scripts/Multiplayer/Racer.lua" then + return("Racer Challenge #16") end -- challenges without border elseif LandDigest == "M-134869715Scripts/Multiplayer/Racer.lua" then @@ -28,6 +34,8 @@ return("Racer Challenge #5") elseif LandDigest == "M479034891Scripts/Multiplayer/Racer.lua" then return("Racer Challenge #6") + elseif LandDigest == "M256715557Scripts/Multiplayer/Racer.lua" then + return("Racer Challenge #15") end end end diff -r 1ff3dd3705b1 -r 99273b7afbff tools/hwmap.hs --- a/tools/hwmap.hs Mon Feb 16 22:33:15 2015 +0300 +++ b/tools/hwmap.hs Thu Apr 02 21:09:56 2015 +0300 @@ -52,7 +52,7 @@ mapM_ putWord8 $ BW.unpack $ BL.toStrict $ Z.compress b mapString :: B.ByteString -mapString = B.pack . Base64.encode . BW.unpack . BL.toStrict . compressWithLength . BL.drop 8 . encode $ drawnMap04 +mapString = B.pack . Base64.encode . BW.unpack . BL.toStrict . compressWithLength . BL.drop 8 . encode $ drawnMap05 main = B.writeFile "out.hwmap" mapString @@ -152,3 +152,13 @@ ] l = Line Solid 0 fm = flip' . mirror + +drawnMap05 = sp ++ fullFill ++ lW + where + w = 320 + sh = 420 + basePoints = [(w, w), (1024 + w `div` 2, 2048 - w), (2048, w), (3072 - w `div` 2, 2048 - w), (4096 - w, w)] + lW = [Line Erasing 60 basePoints] + sp = [SpecialPoints $ basePoints ++ [(1024 + w `div` 2, 2048 - w - sh), (3072 - w `div` 2, 2048 - w - sh), (2048, w + sh)]] + +fullFill = scale 256 $ [Line Solid 63 [(0, 1), (16, 1), (16, 3), (0, 3), (0, 5), (16, 5), (16, 7), (0, 7)]]