# HG changeset patch # User nemo # Date 1532131252 14400 # Node ID 282218ab1b28013ba821a56594ca02e87dbfa49d # Parent 80db7232b4b515dff32e2b1b4f12434c9e9c81c3# Parent 1232ab8206d470fd55fc9721af8e6c49dfc53a91 catchup merge on all of alfadur's cool server changes diff -r 80db7232b4b5 -r 282218ab1b28 ChangeLog.txt --- a/ChangeLog.txt Sat Jul 21 02:19:32 2018 +0300 +++ b/ChangeLog.txt Fri Jul 20 20:00:52 2018 -0400 @@ -4,10 +4,18 @@ Game: + Add new key to show mission panel (default: M) + Add chat command “/help”, displays help for chat commands + + Increase hedgehog limit to 64 + * Functionality of controllers restored + * Fix crash when 2 or more controllers were connected * Fix extreme amounts of droplets when shooting with minigun into ocean world edge * Fix hog being unable to walk after using sniper rifle without firing both shots + * Fix video recorder not working when game audio was disabled + * Fix cursor teleporting to center after leaving game with a video recording * Fix teleport tooltip claiming it doesn't end turn in hog placing phase with inf. attack +Frontend: + * Controllers are detected again + Highlander: * Fix all hogs getting teleport after hog placement phase @@ -52,6 +60,7 @@ + New call: GetAmmo(ammoType): Returns ammo configuration (corresponds to SetAmmo) + New parameter: SetAmmoTexts: 5th param. showExtra: Set to false to hide texts like “Not yet available” + New parameter: ShowMission: 6th param. forceDisplay: Set to true to prevent this particular mission panel to be hidden manually by player + + New Lua library: Achievements * Changed global: lfCurrentHog becomes lfCurHogCrate * Fixed variable: TotalRounds was -1 (instead of 0) in first real round after hog placement phase * AI sometimes intentionally shot hedgehogs with aihDoesntMatter set diff -r 80db7232b4b5 -r 282218ab1b28 INSTALL.md --- a/INSTALL.md Sat Jul 21 02:19:32 2018 +0300 +++ b/INSTALL.md Fri Jul 20 20:00:52 2018 -0400 @@ -79,7 +79,10 @@ - `entropy` - `zlib` >= 0.5.3 and < 0.6 - `regex-tdfa` - + - `binary` >= 0.8.5.1 + - `yaml` >= 0.8.30 + - `aeson` + - `text` >= 1.2 Building -------- diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/CMakeLists.txt --- a/QTfrontend/CMakeLists.txt Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/CMakeLists.txt Fri Jul 20 20:00:52 2018 -0400 @@ -94,6 +94,7 @@ campaign.cpp ui_hwform.cpp ${CMAKE_CURRENT_BINARY_DIR}/hwconsts.cpp + ${CMAKE_CURRENT_BINARY_DIR}/sdlkeys.cpp ) if(MINGW) diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/hwconsts.cpp.in --- a/QTfrontend/hwconsts.cpp.in Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/hwconsts.cpp.in Fri Jul 20 20:00:52 2018 -0400 @@ -16,6 +16,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* + * PLEASE NOTE: hwconsts.cpp is automatically generated from hwconsts.cpp.in. + * Do not edit hwconsts.cpp manually, it will be overwritten when building. + * Edit hwconsts.cpp.in to change the code. + */ + #include #include "hwconsts.h" @@ -37,6 +43,7 @@ bool custom_data = false; int cMaxTeams = 8; +int cMaxHHs = HEDGEHOGS_PER_TEAM * cMaxTeams; int cMinServerVersion = 3; QString * cDefaultAmmoStore = new QString( AMMOLINE_DEFAULT_QT AMMOLINE_DEFAULT_PROB diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/hwconsts.h --- a/QTfrontend/hwconsts.h Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/hwconsts.h Fri Jul 20 20:00:52 2018 -0400 @@ -37,6 +37,7 @@ extern bool custom_data; extern int cMaxTeams; +extern int cMaxHHs; extern int cMinServerVersion; class QStandardItemModel; @@ -73,6 +74,9 @@ #define NETGAME_DEFAULT_PORT 46631 #define HEDGEHOGS_PER_TEAM 8 +//Selected engine exit codes, see hedgewars/uConsts.pas +#define HWENGINE_EXITCODE_OK 0 +#define HWENGINE_EXITCODE_FATAL 52 // see https://en.wikipedia.org/wiki/List_of_colors /*define HW_TEAMCOLOR_ARRAY {0xff007fff, /. azure ./ \ diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/hwform.cpp --- a/QTfrontend/hwform.cpp Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/hwform.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -1707,14 +1707,6 @@ void HWForm::StartMPGame() { - int numHogs = ui.pageMultiplayer->teamsSelect->getNumHedgehogs(); - /* Don't allow to start game with >48 hogs. - TODO: Remove this as soon the engine supports more hogs. */ - if(numHogs > 48) - { - MessageDialog::ShowErrorMessage(QMessageBox::tr("Sorry, Hedgewars can't be played with more than 48 hedgehogs. Please try again with fewer hedgehogs.\n\nCurrent number of hedgehogs: %1").arg(numHogs), this); - return; - } QString ammo; ammo = ui.pageMultiplayer->gameCFG->WeaponsName->itemData( ui.pageMultiplayer->gameCFG->WeaponsName->currentIndex() diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/main.cpp --- a/QTfrontend/main.cpp Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/main.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -161,28 +161,6 @@ } int main(int argc, char *argv[]) { - /* Qt5 Base removed Motif, Plastique. These are now in the Qt style plugins - (Ubuntu: qt5-style-plugins, which was NOT backported by Debian/Ubuntu to stable/LTS). - Windows appears to render best of the remaining options but still isn't quite right. */ - - // Try setting Plastique if available - QStyle* coreStyle; - coreStyle = QStyleFactory::create("Plastique"); - if(coreStyle != 0) { - QApplication::setStyle(coreStyle); - qDebug("Qt style set: Plastique"); - } else { - // Use Windows as fallback. - // FIXME: Under Windows style, some widgets like scrollbars don't render as nicely - coreStyle = QStyleFactory::create("Windows"); - if(coreStyle != 0) { - QApplication::setStyle(coreStyle); - qDebug("Qt style set: Windows"); - } else { - // Windows style should not be missing in Qt5 Base. If it does, something went terribly wrong! - qWarning("No Qt style could be set! Using the default one."); - } - } // Since we're calling this first, closeResources() will be the last thing called after main() returns. atexit(closeResources); @@ -191,8 +169,6 @@ cocoaInit = new CocoaInitializer(); // Creates the autoreleasepool preventing cocoa object leaks on OS X. #endif - SDLInteraction::instance(); - HWApplication app(argc, argv); app.setAttribute(Qt::AA_DontShowIconsInMenus,false); @@ -274,6 +250,29 @@ // end of parameter parsing + // Select Qt style + /* Qt5 Base removed Motif, Plastique. These are now in the Qt style plugins + (Ubuntu: qt5-style-plugins, which was NOT backported by Debian/Ubuntu to stable/LTS). + Windows appears to render best of the remaining options but still isn't quite right. */ + + // Try setting Plastique if available + QStyle* coreStyle; + coreStyle = QStyleFactory::create("Plastique"); + if(coreStyle != 0) { + QApplication::setStyle(coreStyle); + qDebug("Qt style set: Plastique"); + } else { + // Use Windows as fallback. + // FIXME: Under Windows style, some widgets like scrollbars don't render as nicely + coreStyle = QStyleFactory::create("Windows"); + if(coreStyle != 0) { + QApplication::setStyle(coreStyle); + qDebug("Qt style set: Windows"); + } else { + // Windows style should not be missing in Qt5 Base. If it does, something went terribly wrong! + qWarning("No Qt style could be set! Using the default one."); + } + } #ifdef Q_OS_WIN QPixmap pixmap(":/res/splash.png"); @@ -414,6 +413,8 @@ } #endif + SDLInteraction::instance(); + QString style = ""; QString fname; diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/net/recorder.cpp --- a/QTfrontend/net/recorder.cpp Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/net/recorder.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -140,7 +140,10 @@ // Could use a field to use quality instead. maybe quality could override bitrate - or just pass (and set) both. // The library does support using both at once after all. arguments << QString::number(config->rec_Bitrate()*1024); - arguments << (config->recordAudio() ? config->audioCodec() : "no"); + if (config->recordAudio() && (config->isSoundEnabled() || config->isMusicEnabled())) + arguments << config->audioCodec(); + else + arguments << "no"; return arguments; } diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/net/tcpBase.cpp --- a/QTfrontend/net/tcpBase.cpp Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/net/tcpBase.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -235,7 +235,7 @@ // show error message if there was an error that was not an engine's // fatal error - because that one already sent a info via IPC - if ((exitCode != 0) && (exitCode != 2)) + if ((exitCode != HWENGINE_EXITCODE_OK) && (exitCode != HWENGINE_EXITCODE_FATAL)) { // inform user that something bad happened MessageDialog::ShowFatalMessage( @@ -251,9 +251,15 @@ void TCPBase::tcpServerReady() { - disconnect(srvsList.first(), SIGNAL(isReadyNow()), this, SLOT(tcpServerReady())); - - RealStart(); + if (!srvsList.isEmpty()) + { + disconnect(srvsList.first(), SIGNAL(isReadyNow()), this, SLOT(tcpServerReady())); + RealStart(); + } + else + { + qDebug("tcpServerReady() called while srvsList was empty. Not starting TCP server"); + } } void TCPBase::Start(bool couldCancelPreviousRequest) diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/sdlkeys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QTfrontend/sdlkeys.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -0,0 +1,199 @@ +/* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2015 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "sdlkeys.h" + +#include + +char sdlkeys[1024][2][128] = +{ + {"mousel", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Left button")}, + {"mousem", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Middle button")}, + {"mouser", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Right button")}, + {"wheelup", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Wheel up")}, + {"wheeldown", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Wheel down")}, + {"backspace", QT_TRANSLATE_NOOP("binds (keys)", "Backspace")}, + {"tab", QT_TRANSLATE_NOOP("binds (keys)", "Tab")}, + {"clear", QT_TRANSLATE_NOOP("binds (keys)", "Clear")}, + {"return", QT_TRANSLATE_NOOP("binds (keys)", "Return")}, + {"pause", QT_TRANSLATE_NOOP("binds (keys)", "Pause")}, + {"escape", QT_TRANSLATE_NOOP("binds (keys)", "Escape")}, + {"space", QT_TRANSLATE_NOOP("binds (keys)", "Space")}, + {"!", "!"}, + {"\"", "\""}, + {"#", "#"}, + {"$", "$"}, + {"&", "&"}, + {"'", "'"}, + {"(", "("}, + {")", ")"}, + {"*", "*"}, + {"+", "+"}, + {",", ","}, + {"-", "-"}, + {".", "."}, + {"/", "/"}, + {"0", "0"}, + {"1", "1"}, + {"2", "2"}, + {"3", "3"}, + {"4", "4"}, + {"5", "5"}, + {"6", "6"}, + {"7", "7"}, + {"8", "8"}, + {"9", "9"}, + {":", ":"}, + {";", ";"}, + {"<", "<"}, + {"=", "="}, + {">", ">"}, + {"?", "?"}, + {"@", "@"}, + {"[", "["}, + {"\\", "\\"}, + {"]", "]"}, + {"^", "^"}, + {"_", "_"}, + {"`", "`"}, + {"a", "A"}, + {"b", "B"}, + {"c", "C"}, + {"d", "D"}, + {"e", "E"}, + {"f", "F"}, + {"g", "G"}, + {"h", "H"}, + {"i", "I"}, + {"j", "J"}, + {"k", "K"}, + {"l", "L"}, + {"m", "M"}, + {"n", "N"}, + {"o", "O"}, + {"p", "P"}, + {"q", "Q"}, + {"r", "R"}, + {"s", "S"}, + {"t", "T"}, + {"u", "U"}, + {"v", "V"}, + {"w", "W"}, + {"x", "X"}, + {"y", "Y"}, + {"z", "Z"}, + {"delete", QT_TRANSLATE_NOOP("binds (keys)", "Delete")}, + {"keypad_0", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 0")}, + {"keypad_1", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 1")}, + {"keypad_2", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 2")}, + {"keypad_3", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 3")}, + {"keypad_4", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 4")}, + {"keypad_5", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 5")}, + {"keypad_6", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 6")}, + {"keypad_7", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 7")}, + {"keypad_8", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 8")}, + {"keypad_9", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 9")}, + {"keypad_.", QT_TRANSLATE_NOOP("binds (keys)", "Numpad .")}, + {"keypad_/", QT_TRANSLATE_NOOP("binds (keys)", "Numpad /")}, + {"keypad_*", QT_TRANSLATE_NOOP("binds (keys)", "Numpad *")}, + {"keypad_-", QT_TRANSLATE_NOOP("binds (keys)", "Numpad -")}, + {"keypad_+", QT_TRANSLATE_NOOP("binds (keys)", "Numpad +")}, + {"enter", QT_TRANSLATE_NOOP("binds (keys)", "Enter")}, + {"equals", QT_TRANSLATE_NOOP("binds (keys)", "Equals")}, + {"up", QT_TRANSLATE_NOOP("binds (keys)", "Up")}, + {"down", QT_TRANSLATE_NOOP("binds (keys)", "Down")}, + {"right", QT_TRANSLATE_NOOP("binds (keys)", "Right")}, + {"left", QT_TRANSLATE_NOOP("binds (keys)", "Left")}, + {"insert", QT_TRANSLATE_NOOP("binds (keys)", "Insert")}, + {"home", QT_TRANSLATE_NOOP("binds (keys)", "Home")}, + {"end", QT_TRANSLATE_NOOP("binds (keys)", "End")}, + {"page_up", QT_TRANSLATE_NOOP("binds (keys)", "Page up")}, + {"page_down", QT_TRANSLATE_NOOP("binds (keys)", "Page down")}, + {"f1", "F1"}, + {"f2", "F2"}, + {"f3", "F3"}, + {"f4", "F4"}, + {"f5", "F5"}, + {"f6", "F6"}, + {"f7", "F7"}, + {"f8", "F8"}, + {"f9", "F9"}, + {"f10", "F10"}, + {"f11", "F11"}, + {"f12", "F12"}, + {"f13", "F13"}, + {"f14", "F14"}, + {"f15", "F15"}, + {"numlock", QT_TRANSLATE_NOOP("binds (keys)", "Num lock")}, + {"caps_lock", QT_TRANSLATE_NOOP("binds (keys)", "Caps lock")}, + {"scroll_lock", QT_TRANSLATE_NOOP("binds (keys)", "Scroll lock")}, + {"right_shift", QT_TRANSLATE_NOOP("binds (keys)", "Right shift")}, + {"left_shift", QT_TRANSLATE_NOOP("binds (keys)", "Left shift")}, + {"right_ctrl", QT_TRANSLATE_NOOP("binds (keys)", "Right ctrl")}, + {"left_ctrl", QT_TRANSLATE_NOOP("binds (keys)", "Left ctrl")}, + {"right_alt", QT_TRANSLATE_NOOP("binds (keys)", "Right alt")}, + {"left_alt", QT_TRANSLATE_NOOP("binds (keys)", "Left alt")}, + {"right_meta", QT_TRANSLATE_NOOP("binds (keys)", "Right meta")}, + {"left_meta", QT_TRANSLATE_NOOP("binds (keys)", "Left meta")} +}; + +// button name definitions for Microsoft's XBox360 controller +// don't modify button order! +char xb360buttons[10][128] = +{ + QT_TRANSLATE_NOOP("binds (keys)", "A button"), + QT_TRANSLATE_NOOP("binds (keys)", "B button"), + QT_TRANSLATE_NOOP("binds (keys)", "X button"), + QT_TRANSLATE_NOOP("binds (keys)", "Y button"), + QT_TRANSLATE_NOOP("binds (keys)", "LB button"), + QT_TRANSLATE_NOOP("binds (keys)", "RB button"), + QT_TRANSLATE_NOOP("binds (keys)", "Back button"), + QT_TRANSLATE_NOOP("binds (keys)", "Start button"), + QT_TRANSLATE_NOOP("binds (keys)", "Left stick"), + QT_TRANSLATE_NOOP("binds (keys)", "Right stick") +}; + +// axis name definitions for Microsoft's XBox360 controller +// don't modify axis order! +char xbox360axes[][128] = +{ + QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Right)"), + QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Left)"), + QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Down)"), + QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Up)"), + QT_TRANSLATE_NOOP("binds (keys)", "Left trigger"), + QT_TRANSLATE_NOOP("binds (keys)", "Right trigger"), + QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Down)"), + QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Up)"), + QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Right)"), + QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Left)"), +}; +char xb360dpad[128] = QT_TRANSLATE_NOOP("binds (keys)", "D-pad"); + +// Generic controller binding names +//: Game controller axis direction. %1 = axis number, %2 = direction +char controlleraxis[128] = QT_TRANSLATE_NOOP("binds (keys)", "Axis %1 %2"); +//: Game controller button. %1 = button number +char controllerbutton[128] = QT_TRANSLATE_NOOP("binds (keys)", "Button %1"); +//: Game controller D-pad button. %1 = D-pad number, %2 = direction +char controllerhat[128] = QT_TRANSLATE_NOOP("binds (keys)", "D-pad %1 %2"); +char controllerup[128] = QT_TRANSLATE_NOOP("binds (keys)", "Up"); +char controllerdown[128] = QT_TRANSLATE_NOOP("binds (keys)", "Down"); +char controllerleft[128] = QT_TRANSLATE_NOOP("binds (keys)", "Left"); +char controllerright[128] = QT_TRANSLATE_NOOP("binds (keys)", "Right"); + diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/sdlkeys.h --- a/QTfrontend/sdlkeys.h Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/sdlkeys.h Fri Jul 20 20:00:52 2018 -0400 @@ -16,167 +16,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -char sdlkeys[1024][2][128] = -{ - {"mousel", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Left button")}, - {"mousem", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Middle button")}, - {"mouser", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Right button")}, - {"wheelup", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Wheel up")}, - {"wheeldown", QT_TRANSLATE_NOOP("binds (keys)", "Mouse: Wheel down")}, - {"backspace", QT_TRANSLATE_NOOP("binds (keys)", "Backspace")}, - {"tab", QT_TRANSLATE_NOOP("binds (keys)", "Tab")}, - {"clear", QT_TRANSLATE_NOOP("binds (keys)", "Clear")}, - {"return", QT_TRANSLATE_NOOP("binds (keys)", "Return")}, - {"pause", QT_TRANSLATE_NOOP("binds (keys)", "Pause")}, - {"escape", QT_TRANSLATE_NOOP("binds (keys)", "Escape")}, - {"space", QT_TRANSLATE_NOOP("binds (keys)", "Space")}, - {"!", "!"}, - {"\"", "\""}, - {"#", "#"}, - {"$", "$"}, - {"&", "&"}, - {"'", "'"}, - {"(", "("}, - {")", ")"}, - {"*", "*"}, - {"+", "+"}, - {",", ","}, - {"-", "-"}, - {".", "."}, - {"/", "/"}, - {"0", "0"}, - {"1", "1"}, - {"2", "2"}, - {"3", "3"}, - {"4", "4"}, - {"5", "5"}, - {"6", "6"}, - {"7", "7"}, - {"8", "8"}, - {"9", "9"}, - {":", ":"}, - {";", ";"}, - {"<", "<"}, - {"=", "="}, - {">", ">"}, - {"?", "?"}, - {"@", "@"}, - {"[", "["}, - {"\\", "\\"}, - {"]", "]"}, - {"^", "^"}, - {"_", "_"}, - {"`", "`"}, - {"a", "A"}, - {"b", "B"}, - {"c", "C"}, - {"d", "D"}, - {"e", "E"}, - {"f", "F"}, - {"g", "G"}, - {"h", "H"}, - {"i", "I"}, - {"j", "J"}, - {"k", "K"}, - {"l", "L"}, - {"m", "M"}, - {"n", "N"}, - {"o", "O"}, - {"p", "P"}, - {"q", "Q"}, - {"r", "R"}, - {"s", "S"}, - {"t", "T"}, - {"u", "U"}, - {"v", "V"}, - {"w", "W"}, - {"x", "X"}, - {"y", "Y"}, - {"z", "Z"}, - {"delete", QT_TRANSLATE_NOOP("binds (keys)", "Delete")}, - {"keypad_0", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 0")}, - {"keypad_1", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 1")}, - {"keypad_2", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 2")}, - {"keypad_3", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 3")}, - {"keypad_4", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 4")}, - {"keypad_5", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 5")}, - {"keypad_6", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 6")}, - {"keypad_7", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 7")}, - {"keypad_8", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 8")}, - {"keypad_9", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 9")}, - {"keypad_.", QT_TRANSLATE_NOOP("binds (keys)", "Numpad .")}, - {"keypad_/", QT_TRANSLATE_NOOP("binds (keys)", "Numpad /")}, - {"keypad_*", QT_TRANSLATE_NOOP("binds (keys)", "Numpad *")}, - {"keypad_-", QT_TRANSLATE_NOOP("binds (keys)", "Numpad -")}, - {"keypad_+", QT_TRANSLATE_NOOP("binds (keys)", "Numpad +")}, - {"enter", QT_TRANSLATE_NOOP("binds (keys)", "Enter")}, - {"equals", QT_TRANSLATE_NOOP("binds (keys)", "Equals")}, - {"up", QT_TRANSLATE_NOOP("binds (keys)", "Up")}, - {"down", QT_TRANSLATE_NOOP("binds (keys)", "Down")}, - {"right", QT_TRANSLATE_NOOP("binds (keys)", "Right")}, - {"left", QT_TRANSLATE_NOOP("binds (keys)", "Left")}, - {"insert", QT_TRANSLATE_NOOP("binds (keys)", "Insert")}, - {"home", QT_TRANSLATE_NOOP("binds (keys)", "Home")}, - {"end", QT_TRANSLATE_NOOP("binds (keys)", "End")}, - {"page_up", QT_TRANSLATE_NOOP("binds (keys)", "Page up")}, - {"page_down", QT_TRANSLATE_NOOP("binds (keys)", "Page down")}, - {"f1", "F1"}, - {"f2", "F2"}, - {"f3", "F3"}, - {"f4", "F4"}, - {"f5", "F5"}, - {"f6", "F6"}, - {"f7", "F7"}, - {"f8", "F8"}, - {"f9", "F9"}, - {"f10", "F10"}, - {"f11", "F11"}, - {"f12", "F12"}, - {"f13", "F13"}, - {"f14", "F14"}, - {"f15", "F15"}, - {"numlock", QT_TRANSLATE_NOOP("binds (keys)", "Num lock")}, - {"caps_lock", QT_TRANSLATE_NOOP("binds (keys)", "Caps lock")}, - {"scroll_lock", QT_TRANSLATE_NOOP("binds (keys)", "Scroll lock")}, - {"right_shift", QT_TRANSLATE_NOOP("binds (keys)", "Right shift")}, - {"left_shift", QT_TRANSLATE_NOOP("binds (keys)", "Left shift")}, - {"right_ctrl", QT_TRANSLATE_NOOP("binds (keys)", "Right ctrl")}, - {"left_ctrl", QT_TRANSLATE_NOOP("binds (keys)", "Left ctrl")}, - {"right_alt", QT_TRANSLATE_NOOP("binds (keys)", "Right alt")}, - {"left_alt", QT_TRANSLATE_NOOP("binds (keys)", "Left alt")}, - {"right_meta", QT_TRANSLATE_NOOP("binds (keys)", "Right meta")}, - {"left_meta", QT_TRANSLATE_NOOP("binds (keys)", "Left meta")} -}; +#ifndef SDLKEYS_H +#define SDLKEYS_H -// button name definitions for Microsoft's XBox360 controller -// don't modify button order! -char xb360buttons[][128] = -{ - QT_TRANSLATE_NOOP("binds (keys)", "A button"), - QT_TRANSLATE_NOOP("binds (keys)", "B button"), - QT_TRANSLATE_NOOP("binds (keys)", "X button"), - QT_TRANSLATE_NOOP("binds (keys)", "Y button"), - QT_TRANSLATE_NOOP("binds (keys)", "LB button"), - QT_TRANSLATE_NOOP("binds (keys)", "RB button"), - QT_TRANSLATE_NOOP("binds (keys)", "Back button"), - QT_TRANSLATE_NOOP("binds (keys)", "Start button"), - QT_TRANSLATE_NOOP("binds (keys)", "Left stick"), - QT_TRANSLATE_NOOP("binds (keys)", "Right stick") -}; +extern char sdlkeys[1024][2][128]; +extern char xb360buttons[10][128]; +extern char xbox360axes[10][128]; +extern char xb360dpad[128]; +extern char controlleraxis[128]; +extern char controllerbutton[128]; +extern char controllerhat[128]; +extern char controllerup[128]; +extern char controllerdown[128]; +extern char controllerleft[128]; +extern char controllerright[128]; -// axis name definitions for Microsoft's XBox360 controller -// don't modify axis order! -char xbox360axes[][128] = -{ - QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Right)"), - QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Left)"), - QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Down)"), - QT_TRANSLATE_NOOP("binds (keys)", "Left stick (Up)"), - QT_TRANSLATE_NOOP("binds (keys)", "Left trigger"), - QT_TRANSLATE_NOOP("binds (keys)", "Right trigger"), - QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Down)"), - QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Up)"), - QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Right)"), - QT_TRANSLATE_NOOP("binds (keys)", "Right stick (Left)"), -}; -char xb360dpad[128] = QT_TRANSLATE_NOOP("binds (keys)", "DPad"); +#endif diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/ui/widget/chatwidget.cpp --- a/QTfrontend/ui/widget/chatwidget.cpp Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/ui/widget/chatwidget.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -381,7 +381,10 @@ return QString("%1").arg(nickname.toHtmlEscaped()); } -const QRegExp HWChatWidget::URLREGEXP = QRegExp("(http(s)?://)?(www\\.)?((([^/:?&#]+\\.)?hedgewars\\.org|code\\.google\\.com|googlecode\\.com|hh\\.unit22\\.org)(/[^ ]*)?)"); +// Regex to make some URLs clickable for selected domains: +// - hedgewars.org (official website) +// - hh.unit22.org (community addon server) +const QRegExp HWChatWidget::URLREGEXP = QRegExp("(http(s)?://)?(www\\.)?((([^/:?&#]+\\.)?hedgewars\\.org|hh\\.unit22\\.org)(/[^ ]*)?)"); bool HWChatWidget::containsHighlight(const QString & sender, const QString & message) { @@ -401,7 +404,7 @@ QString HWChatWidget::messageToHTML(const QString & message) { QString formattedStr = message.toHtmlEscaped(); - // link some urls + // link some URLs formattedStr = formattedStr.replace(URLREGEXP, "\\4"); return formattedStr; } diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/ui/widget/frameTeam.cpp --- a/QTfrontend/ui/widget/frameTeam.cpp Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/ui/widget/frameTeam.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -126,7 +126,7 @@ bool FrameTeams::isFullTeams() const { - return teamToWidget.size() >= 8; + return teamToWidget.size() >= cMaxTeams; } void FrameTeams::emitTeamColorChanged(const HWTeam& team) diff -r 80db7232b4b5 -r 282218ab1b28 QTfrontend/util/SDLInteraction.cpp --- a/QTfrontend/util/SDLInteraction.cpp Sat Jul 21 02:19:32 2018 +0300 +++ b/QTfrontend/util/SDLInteraction.cpp Fri Jul 20 20:00:52 2018 -0400 @@ -25,6 +25,7 @@ #include "SDL_mixer.h" #include "HWApplication.h" +#include "sdlkeys.h" #include "hwform.h" /* you know, we could just put a config singleton lookup function in gameuiconfig or something... */ #include "gameuiconfig.h" @@ -32,12 +33,6 @@ #include "physfsrwops.h" -extern char sdlkeys[1024][2][128]; -extern char xb360buttons[][128]; -extern char xb360dpad[128]; -extern char xbox360axes[][128]; - - SDLInteraction & SDLInteraction::instance() { static SDLInteraction instance; @@ -105,19 +100,23 @@ QStringList result; #if SDL_VERSION_ATLEAST(2, 0, 0) -// TODO or not TODO? -#else int i = 0; while(i < 1024 && sdlkeys[i][1][0] != '\0') i++; // Iterate through all game controllers + qDebug("Detecting controllers ..."); for(int jid = 0; jid < SDL_NumJoysticks(); jid++) { SDL_Joystick* joy = SDL_JoystickOpen(jid); - // Retrieve the game controller's name and strip "Controller (...)" that's added by some drivers (English only) - QString joyname = QString(SDL_JoystickName(jid)).replace(QRegExp("^Controller \\((.*)\\)$"), "\\1"); + // Retrieve the game controller's name + QString joyname = QString(SDL_JoystickNameForIndex(jid)); + + // Strip "Controller (...)" that's added by some drivers (English only) + joyname.replace(QRegExp("^Controller \\((.*)\\)$"), "\\1"); + + qDebug("- Controller no. %d: %s", jid, qPrintable(joyname)); // Connected Xbox 360 controller? Use specific button names then // Might be interesting to add 'named' buttons for the most often used gamepads @@ -129,39 +128,38 @@ // Register entries for missing axes not assigned to sticks of this joystick/gamepad for(int aid = 0; aid < SDL_JoystickNumAxes(joy) && i < 1021; aid++) { - // Again store the part of the string not changing for multiple uses - QString axis = prefix + HWApplication::translate("binds (keys)", "Axis") + QString(" %1 ").arg(aid + 1); + QString axis = prefix + HWApplication::translate("binds (keys)", controlleraxis).arg(aid + 1); // Entry for "Axis Up" sprintf(sdlkeys[i][0], "j%da%du", jid, aid); - sprintf(sdlkeys[i++][1], "%s", ((isxb && aid < 5) ? (prefix + HWApplication::translate("binds (keys)", xbox360axes[aid * 2])) : axis + HWApplication::translate("binds (keys)", "(Up)")).toUtf8().constData()); + sprintf(sdlkeys[i++][1], "%s", ((isxb && aid < 5) ? (prefix + HWApplication::translate("binds (keys)", xbox360axes[aid * 2])) : (axis.arg(HWApplication::translate("binds (keys)", controllerup)))).toUtf8().constData()); // Entry for "Axis Down" sprintf(sdlkeys[i][0], "j%da%dd", jid, aid); - sprintf(sdlkeys[i++][1], "%s", ((isxb && aid < 5) ? (prefix + HWApplication::translate("binds (keys)", xbox360axes[aid * 2 + 1])) : axis + HWApplication::translate("binds (keys)", "(Down)")).toUtf8().constData()); + sprintf(sdlkeys[i++][1], "%s", ((isxb && aid < 5) ? (prefix + HWApplication::translate("binds (keys)", xbox360axes[aid * 2 + 1])) : (axis.arg(HWApplication::translate("binds (keys)", controllerdown)))).toUtf8().constData()); } // Register entries for all coolie hats of this joystick/gamepad for(int hid = 0; hid < SDL_JoystickNumHats(joy) && i < 1019; hid++) { // Again store the part of the string not changing for multiple uses - QString hat = prefix + (isxb ? (HWApplication::translate("binds (keys)", xb360dpad) + QString(" ")) : HWApplication::translate("binds (keys)", "Hat") + QString(" %1 ").arg(hid + 1)); + QString hat = prefix + (isxb ? (HWApplication::translate("binds (keys)", xb360dpad) + QString(" ")) : HWApplication::translate("binds (keys)", controllerhat).arg(hid + 1)); // Entry for "Hat Up" sprintf(sdlkeys[i][0], "j%dh%du", jid, hid); - sprintf(sdlkeys[i++][1], "%s", (hat + HWApplication::translate("binds (keys)", "(Up)")).toUtf8().constData()); + sprintf(sdlkeys[i++][1], "%s", hat.arg(HWApplication::translate("binds (keys)", controllerup)).toUtf8().constData()); // Entry for "Hat Down" sprintf(sdlkeys[i][0], "j%dh%dd", jid, hid); - sprintf(sdlkeys[i++][1], "%s", (hat + HWApplication::translate("binds (keys)", "(Down)")).toUtf8().constData()); + sprintf(sdlkeys[i++][1], "%s", hat.arg(HWApplication::translate("binds (keys)", controllerdown)).toUtf8().constData()); // Entry for "Hat Left" sprintf(sdlkeys[i][0], "j%dh%dl", jid, hid); - sprintf(sdlkeys[i++][1], "%s", (hat + HWApplication::translate("binds (keys)", "(Left)")).toUtf8().constData()); + sprintf(sdlkeys[i++][1], "%s", hat.arg(HWApplication::translate("binds (keys)", controllerleft)).toUtf8().constData()); // Entry for "Hat Right" sprintf(sdlkeys[i][0], "j%dh%dr", jid, hid); - sprintf(sdlkeys[i++][1], "%s", (hat + HWApplication::translate("binds (keys)", "(Right)")).toUtf8().constData()); + sprintf(sdlkeys[i++][1], "%s", hat.arg(HWApplication::translate("binds (keys)", controllerright)).toUtf8().constData()); } // Register entries for all buttons of this joystick/gamepad @@ -169,7 +167,7 @@ { // Buttons sprintf(sdlkeys[i][0], "j%db%d", jid, bid); - sprintf(sdlkeys[i++][1], "%s", (prefix + ((isxb && bid < 10) ? (HWApplication::translate("binds (keys)", xb360buttons[bid]) + QString(" ")) : HWApplication::translate("binds (keys)", "Button") + QString(" %1").arg(bid + 1))).toUtf8().constData()); + sprintf(sdlkeys[i++][1], "%s", (prefix + ((isxb && bid < 10) ? (HWApplication::translate("binds (keys)", xb360buttons[bid]) + QString(" ")) : HWApplication::translate("binds (keys)", controllerbutton).arg(bid + 1))).toUtf8().constData()); } // Close the game controller as we no longer need it SDL_JoystickClose(joy); diff -r 80db7232b4b5 -r 282218ab1b28 README.md --- a/README.md Sat Jul 21 02:19:32 2018 +0300 +++ b/README.md Fri Jul 20 20:00:52 2018 -0400 @@ -69,6 +69,24 @@ * T: Chat * U: Team chat +### Default controller controls +Hedgewars also supports controllers. By default, Hedgewars sets up a few +controls for your first (!) connected controller. +This default might not be ideal, we recommend you set manual controls +in the settings menu. + +* 1st D-pad: Walk and aim +* 2nd D-pad / 1st thumbstick: Move camera/cursor +* Button 1: Long jump +* Button 2: High jump +* Button 3: Attack +* Button 4: Ammo menu +* Button 5: Precise +* Button 6: Select target, or pick weapon (in ammo menu) +* Button 7: Switch hedgehog +* Button 8: Find hedgehog / toggle auto camera +* Button 9: Mission panel + For the full list, go to the Hedgewars settings. Also read the weapon tooltips for weapon-specific controls. diff -r 80db7232b4b5 -r 282218ab1b28 cmake_modules/cpackvars.cmake --- a/cmake_modules/cpackvars.cmake Sat Jul 21 02:19:32 2018 +0300 +++ b/cmake_modules/cpackvars.cmake Fri Jul 20 20:00:52 2018 -0400 @@ -19,8 +19,8 @@ if(WIN32 AND NOT UNIX) set(CPACK_NSIS_DISPLAY_NAME "Hedgewars") - set(CPACK_NSIS_HELP_LINK "http://www.hedgewars.org/") - set(CPACK_NSIS_URL_INFO_ABOUT "http://www.hedgewars.org/") + set(CPACK_NSIS_HELP_LINK "https://www.hedgewars.org/") + set(CPACK_NSIS_URL_INFO_ABOUT "https://www.hedgewars.org/") set(CPACK_NSIS_CONTACT "unC0Rr@gmail.com") set(CPACK_NSIS_MODIFY_PATH OFF) set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/Actions.hs --- a/gameServer/Actions.hs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/Actions.hs Fri Jul 20 20:00:52 2018 -0400 @@ -634,7 +634,7 @@ mapM_ processAction [ CheckBanned True - , AnswerClients [sendChan cl] ["CONNECTED", "Hedgewars server http://www.hedgewars.org/", serverVersion] + , AnswerClients [sendChan cl] ["CONNECTED", "Hedgewars server https://www.hedgewars.org/", serverVersion] ] else processAction $ ByeClient $ loc "Reconnected too fast" diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/CommandHelp.hs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gameServer/CommandHelp.hs Fri Jul 20 20:00:52 2018 -0400 @@ -0,0 +1,105 @@ +{- + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2015 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + \-} + +{-# LANGUAGE OverloadedStrings #-} +module CommandHelp where + +import qualified Data.ByteString.Char8 as B + +import CoreTypes +import Utils + +-- List and documentation of chat commands + +cmdHelpSharedPlayer :: [B.ByteString] +cmdHelpSharedPlayer = [ + loc "/info : Show info about player", + loc "/me : Chat action, e.g. '/me eats pizza' becomes '* Player eats pizza'", + loc "/rnd: Flip a virtual coin and reply with 'heads' or 'tails'", + loc "/rnd [A] [B] [C] [...]: Reply with a random word from the given list", + loc "/watch : Watch a demo stored on the server with the given ID", + loc "/help: Show chat command help" + ] + +cmdHelpRoomOnlyPlayer :: [B.ByteString] +cmdHelpRoomOnlyPlayer = [ + -- For everyone + loc "/callvote [arguments]: Start a vote", + loc "/vote : Vote 'yes' or 'no' for active vote", + -- For room master only + loc "/greeting : Set greeting message to be shown to players who join the room", + loc "/delegate : Surrender room control to player", + loc "/maxteams : Limit maximum number of teams to N" + ] + +cmdHelpSharedAdmin :: [B.ByteString] +cmdHelpSharedAdmin = [ + loc "/global : Send global chat message which can be seen by everyone on the server", + loc "/registered_only: Toggle 'registered only' state. If enabled, only registered players can join server", + loc "/super_power: Activate your super power. With it you can enter any room and are protected from kicking. Expires when you leave server", + -- TODO: Add help for /save + loc "/save " + -- TODO: Add /restart_server? This command seems broken at the moment + ] + +cmdHelpLobbyOnlyAdmin :: [B.ByteString] +cmdHelpLobbyOnlyAdmin = [ + loc "/stats: Query server stats" + ] + +cmdHelpRoomOnlyAdmin :: [B.ByteString] +cmdHelpRoomOnlyAdmin = [ + loc "/force : Force vote result for active vote", + loc "/fix: Force this room to stay open when it is empty", + loc "/unfix: Undo the /fix command", + loc "/saveroom : Save room configuration into a file", + loc "/loadroom : Load room configuration from a file", + -- TODO: Add help for /delete + loc "/delete " + ] + +cmdHelpHeaderLobby :: [B.ByteString] +cmdHelpHeaderLobby = [ loc "List of lobby chat commands:" ] + +cmdHelpHeaderRoom :: [B.ByteString] +cmdHelpHeaderRoom = [ loc "List of room chat commands:" ] + +cmdHelpHeaderAdmin :: [B.ByteString] +cmdHelpHeaderAdmin = [ loc "Commands for server admins only:" ] + +-- Put it all together +-- Lobby commands +cmdHelpLobbyPlayer :: [B.ByteString] +cmdHelpLobbyPlayer = cmdHelpHeaderLobby ++ cmdHelpSharedPlayer + +cmdHelpLobbyAdmin :: [B.ByteString] +cmdHelpLobbyAdmin = cmdHelpLobbyPlayer ++ cmdHelpHeaderAdmin ++ cmdHelpLobbyOnlyAdmin ++ cmdHelpSharedAdmin + +-- Room commands +cmdHelpRoomPlayer :: [B.ByteString] +cmdHelpRoomPlayer = cmdHelpHeaderRoom ++ cmdHelpRoomOnlyPlayer ++ cmdHelpSharedPlayer + +cmdHelpRoomAdmin :: [B.ByteString] +cmdHelpRoomAdmin = cmdHelpRoomPlayer ++ cmdHelpHeaderAdmin ++ cmdHelpRoomOnlyAdmin ++ cmdHelpSharedAdmin + +-- Helper functions for chat command handler +cmdHelpActionEntry :: [ClientChan] -> B.ByteString -> Action +cmdHelpActionEntry chan msg = AnswerClients chan [ "CHAT", "[server]", B.concat [ " ", msg ] ] + +cmdHelpActionList :: [ClientChan] -> [B.ByteString] -> [Action] +cmdHelpActionList chan list = map (cmdHelpActionEntry chan) list diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/Consts.hs --- a/gameServer/Consts.hs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/Consts.hs Fri Jul 20 20:00:52 2018 -0400 @@ -23,3 +23,12 @@ serverVersion :: B.ByteString serverVersion = "3" + +cHogsPerTeam :: Int +cHogsPerTeam = 8 + +cMaxTeams :: Int +cMaxTeams = 8 + +cMaxHHs :: Int +cMaxHHs = cHogsPerTeam * cMaxTeams diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/CoreTypes.hs --- a/gameServer/CoreTypes.hs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/CoreTypes.hs Fri Jul 20 20:00:52 2018 -0400 @@ -310,8 +310,8 @@ ServerInfo True False - "

http://www.hedgewars.org/

" - "

Hedgewars 0.9.24 is out! Please update.

Download page here" + "

https://www.hedgewars.org/

" + "

Hedgewars 0.9.24 is out! Please update.

Download page here" 55 -- latestReleaseVersion 41 -- earliestCompatibleVersion 46631 diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/HWProtoCore.hs --- a/gameServer/HWProtoCore.hs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/HWProtoCore.hs Fri Jul 20 20:00:52 2018 -0400 @@ -87,9 +87,14 @@ | otherwise = let (c, p) = extractParameters msg in if B.null p then handleCmd ["CALLVOTE", c] else handleCmd ["CALLVOTE", c, p] h "VOTE" msg | not $ B.null msg = handleCmd ["VOTE", upperCase msg] + | otherwise = handleCmd ["VOTE", ""] + h "FORCE" msg | not $ B.null msg = handleCmd ["VOTE", upperCase msg, "FORCE"] + | otherwise = handleCmd ["VOTE", "", "FORCE"] + 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 "HELP" _ = handleCmd ["HELP"] h "RESTART_SERVER" "YES" = handleCmd ["RESTART_SERVER"] h "REGISTERED_ONLY" _ = serverAdminOnly $ do cl <- thisClient @@ -98,7 +103,8 @@ , AnswerClients [sendChan cl] ["CHAT", "[server]", "'Registered only' state toggled"] ] h "SUPER_POWER" _ = serverAdminOnly $ return [ModifyClient (\c -> c{hasSuperPower = True})] - h c p = return [Warning $ B.concat ["Unknown cmd: /", c, " ", p]] + h c p = return [Warning $ B.concat [ loc "Unknown command:", " /", c, " ", p, "
", loc "Say '/help' in chat for a list of commands" ]] + extractParameters p = let (a, b) = B.break (== ' ') p in (upperCase a, B.dropWhile (== ' ') b) @@ -113,14 +119,14 @@ let clRoom = room rnc roomId let roomMasterSign = if isMaster cl then "+" else "" let adminSign = if isAdministrator cl then "@" else "" - let rInfo = if roomId /= lobbyId then B.concat [adminSign, roomMasterSign, "room ", name clRoom] else adminSign `B.append` "lobby" + let rInfo = if roomId /= lobbyId then B.concat [adminSign, roomMasterSign, loc "room", " ", name clRoom] else adminSign `B.append` (loc "lobby") let roomStatus = if isJust $ gameInfo clRoom then - if teamsInGame cl > 0 then "(playing)" else "(spectating)" + if teamsInGame cl > 0 then (loc "(playing)") else (loc "(spectating)") else "" let hostStr = if isAdminAsking then host cl else B.empty if noSuchClient then - return [] + answerClient [ "CHAT", "[server]", loc "Player is not online." ] else answerClient [ "INFO", diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/HWProtoInRoomState.hs --- a/gameServer/HWProtoInRoomState.hs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/HWProtoInRoomState.hs Fri Jul 20 20:00:52 2018 -0400 @@ -27,11 +27,13 @@ import Control.Monad.Reader -------------------------------------- import CoreTypes +import Consts import Utils import HandlerUtils import RoomsAndClients import EngineInteraction import Votes +import CommandHelp startGame :: Reader (ClientIndex, IRnC) [Action] startGame = do @@ -144,7 +146,7 @@ AnswerClients roomChans ["HH_NUM", tName, showB $ hhnum newTeam] ] where - canAddNumber rt = (48::Int) - (sum $ map hhnum rt) + canAddNumber rt = (cMaxHHs) - (sum $ map hhnum rt) findTeam = find (\t -> tName == teamname t) . teams dif = readInt_ difStr hhsList [] = [] @@ -153,7 +155,7 @@ newTeamHHNum rt p = min p (canAddNumber rt) maxTeams r | roomProto r < 38 = 6 - | otherwise = 8 + | otherwise = cMaxTeams handleCmd_inRoom ["REMOVE_TEAM", tName] = do @@ -196,7 +198,7 @@ [ProtocolError $ loc "You're not the room master!"] else if isNothing maybeTeam then [] - else if hhNumber < 1 || hhNumber > 8 || hhNumber > canAddNumber r + hhnum team then + else if hhNumber < 1 || hhNumber > cHogsPerTeam || hhNumber > canAddNumber r + hhnum team then [AnswerClients clChan ["HH_NUM", teamName, showB $ hhnum team]] else [ModifyRoom $ modifyTeam team{hhnum = hhNumber}, @@ -204,7 +206,7 @@ where hhNumber = readInt_ numberStr findTeam = find (\t -> teamName == teamname t) . teams - canAddNumber = (-) 48 . sum . map hhnum . teams + canAddNumber = (-) cMaxHHs . sum . map hhnum . teams @@ -393,7 +395,7 @@ handleCmd_inRoom ["MAXTEAMS", n] = roomAdminOnly $ do cl <- thisClient let m = readInt_ n - if m < 2 || m > 8 then + if m < 2 || m > cMaxTeams then return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/maxteams: specify number from 2 to 8"]] else return [ModifyRoom (\r -> r{teamsNumberLimit = m})] @@ -404,6 +406,13 @@ handleCmd_inRoom ["UNFIX"] = serverAdminOnly $ return [ModifyRoom (\r -> r{isSpecial = False})] +handleCmd_inRoom ["HELP"] = do + cl <- thisClient + if isAdministrator cl then + return (cmdHelpActionList [sendChan cl] cmdHelpRoomAdmin) + else + return (cmdHelpActionList [sendChan cl] cmdHelpRoomPlayer) + handleCmd_inRoom ["GREETING", msg] = do cl <- thisClient rm <- thisRoom @@ -476,7 +485,7 @@ cl <- thisClient let h = readInt_ hhs - if h > 0 && h <= 8 then + if h > 0 && h <= cHogsPerTeam then startVote $ VoteHedgehogsPerTeam h else return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/callvote hedgehogs: Specify number from 1 to 8."]] @@ -487,8 +496,13 @@ let b = if m == "YES" then Just True else if m == "NO" then Just False else Nothing if isJust b then voted (p == ["FORCE"]) (fromJust b) - else - return [AnswerClients [sendChan cl] ["CHAT", "[server]", "/vote: Please use 'yes' or 'no'."]] + else + return [AnswerClients [sendChan cl] ["CHAT", "[server]", + if (p == ["FORCE"]) then + loc "/force: Please use 'yes' or 'no'." + else + loc "/vote: Please use 'yes' or 'no'." + ]] handleCmd_inRoom ["SAVE", stateName, location] = serverAdminOnly $ do diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/HWProtoLobbyState.hs --- a/gameServer/HWProtoLobbyState.hs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/HWProtoLobbyState.hs Fri Jul 20 20:00:52 2018 -0400 @@ -29,6 +29,7 @@ import HandlerUtils import RoomsAndClients import EngineInteraction +import CommandHelp handleCmd_lobby :: CmdHandler @@ -166,6 +167,13 @@ c <- liftM sendChan thisClient return [Random [c] rs] +handleCmd_lobby ["HELP"] = do + cl <- thisClient + if isAdministrator cl then + return (cmdHelpActionList [sendChan cl] cmdHelpLobbyAdmin) + else + return (cmdHelpActionList [sendChan cl] cmdHelpLobbyPlayer) + --------------------------- -- Administrator's stuff -- diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/Votes.hs --- a/gameServer/Votes.hs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/Votes.hs Fri Jul 20 20:00:52 2018 -0400 @@ -28,6 +28,7 @@ import Data.Maybe import Control.Applicative ------------------- +import Consts import Utils import CoreTypes import HandlerUtils @@ -118,7 +119,7 @@ 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 + ) $ if length curteams * h > cMaxHHs then [] else curteams ; curteams = if isJust $ gameInfo rm then diff -r 80db7232b4b5 -r 282218ab1b28 gameServer/hedgewars-server.cabal --- a/gameServer/hedgewars-server.cabal Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer/hedgewars-server.cabal Fri Jul 20 20:00:52 2018 -0400 @@ -2,7 +2,7 @@ Version: 0.1 Synopsis: hedgewars server Description: hedgewars server -Homepage: http://www.hedgewars.org/ +Homepage: https://www.hedgewars.org/ License: GPL-2 Author: unC0Rr Maintainer: a.korotaev@hedgewars.org diff -r 80db7232b4b5 -r 282218ab1b28 gameServer2/src/protocol/messages.rs --- a/gameServer2/src/protocol/messages.rs Sat Jul 21 02:19:32 2018 +0300 +++ b/gameServer2/src/protocol/messages.rs Fri Jul 20 20:00:52 2018 -0400 @@ -260,7 +260,7 @@ Pong => msg!["PONG"], Connected(protocol_version) => msg![ "CONNECTED", - "Hedgewars server http://www.hedgewars.org/", + "Hedgewars server https://www.hedgewars.org/", protocol_version], Bye(msg) => msg!["BYE", msg], Nick(nick) => msg!["NICK", nick], diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/ArgParsers.pas --- a/hedgewars/ArgParsers.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/ArgParsers.pas Fri Jul 20 20:00:52 2018 -0400 @@ -34,6 +34,7 @@ implementation uses uVariables, uTypes, uUtils, uSound, uConsts; var isInternal: Boolean; + helpCommandUsed: Boolean; {$IFDEF HWLIBRARY} @@ -100,8 +101,9 @@ WriteLn(stdout, ' --help'); WriteLn(stdout, ''); WriteLn(stdout, 'For more detailed help and examples go to:'); - WriteLn(stdout, 'http://hedgewars.org/kb/CommandLineOptions'); - GameType:= gmtSyntax; + WriteLn(stdout, 'https://hedgewars.org/kb/CommandLineOptions'); + GameType:= gmtSyntaxHelp; + helpCommandUsed:= true; end; procedure setDepth(var paramIndex: LongInt); @@ -357,12 +359,13 @@ inc(paramIndex); end; if wrongParameter = true then - GameType:= gmtSyntax; + GameType:= gmtBadSyntax; end; procedure GetParams; begin isInternal:= (ParamStr(1) = '--internal'); + helpCommandUsed:= false; UserPathPrefix := _S'.'; PathPrefix := cDefaultPathPrefix; @@ -372,18 +375,19 @@ if (isInternal) and (ParamCount<=1) then begin WriteLn(stderr, '--internal should not be manually used'); - GameType := gmtSyntax; + GameType := gmtBadSyntax; end; - if (not cTestLua) and (not isInternal) and (recordFileName = '') then - begin - WriteLn(stderr, 'You must specify a replay file'); - GameType := gmtSyntax; - end - else if (recordFileName <> '') then - WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"'); + if (not helpCommandUsed) then + if (not cTestLua) and (not isInternal) and (recordFileName = '') then + begin + WriteLn(stderr, 'You must specify a replay file'); + GameType := gmtBadSyntax; + end + else if (recordFileName <> '') then + WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"'); - if (GameType = gmtSyntax) then + if (GameType = gmtBadSyntax) then WriteLn(stderr, 'Please use --help to see possible arguments and their usage'); (* diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/LuaPas.pas --- a/hedgewars/LuaPas.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/LuaPas.pas Fri Jul 20 20:00:52 2018 -0400 @@ -114,7 +114,7 @@ (* ** $Id: lua.h,v 1.216 2006/01/10 12:50:13 roberto Exp $ ** Lua - An Extensible Extension Language -** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** Lua.org, PUC-Rio, Brazil (https://www.lua.org) ** See Copyright Notice at the end of this file *) diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/SDLh.pas --- a/hedgewars/SDLh.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/SDLh.pas Fri Jul 20 20:00:52 2018 -0400 @@ -541,8 +541,8 @@ ///////////////////////////////////////////////////////////////// // two important reference points for the wanderers of this area -// http://www.freepascal.org/docs-html/ref/refsu5.html -// http://www.freepascal.org/docs-html/prog/progsu144.html +// https://www.freepascal.org/docs-html/ref/refsu5.html +// https://www.freepascal.org/docs-html/prog/progsu144.html type PSDL_Window = Pointer; @@ -553,6 +553,7 @@ TSDL_FingerId = Int64; TSDL_Keycode = LongInt; TSDL_Scancode = LongInt; + TSDL_JoystickID = LongInt; TSDL_eventaction = (SDL_ADDEVENT, SDL_PEEPEVENT, SDL_GETEVENT); @@ -765,7 +766,7 @@ TSDL_ControllerAxisEvent = record type_: LongWord; timestamp: LongWord; - which: LongInt; + which: TSDL_JoystickID; axis, padding1, padding2, padding3: Byte; value: SmallInt; padding4: Word; @@ -774,14 +775,14 @@ TSDL_ControllerButtonEvent = record type_: LongWord; timestamp: LongWord; - which: LongInt; + which: TSDL_JoystickID; button, states, padding1, padding2: Byte; end; TSDL_ControllerDeviceEvent = record type_: LongWord; timestamp: LongWord; - which: SmallInt; + which: LongInt; end; TSDL_JoyDeviceEvent = TSDL_ControllerDeviceEvent; @@ -829,17 +830,17 @@ TSDL_JoyAxisEvent = record type_: LongWord; timestamp: LongWord; - which: LongWord; + which: TSDL_JoystickID; axis: Byte; padding1, padding2, padding3: Byte; - value: LongInt; + value: SmallInt; padding4: Word; end; TSDL_JoyBallEvent = record type_: LongWord; timestamp: LongWord; - which: LongWord; + which: TSDL_JoystickID; ball: Byte; padding1, padding2, padding3: Byte; xrel, yrel: SmallInt; @@ -848,7 +849,7 @@ TSDL_JoyHatEvent = record type_: LongWord; timestamp: LongWord; - which: LongWord; + which: TSDL_JoystickID; hat: Byte; value: Byte; padding1, padding2: Byte; @@ -857,10 +858,11 @@ TSDL_JoyButtonEvent = record type_: LongWord; timestamp: LongWord; - which: Byte; + which: TSDL_JoystickID; button: Byte; state: Byte; padding1: Byte; + padding2: Byte; end; TSDL_QuitEvent = record @@ -1165,7 +1167,7 @@ procedure SDL_UnlockAudio; cdecl; external SDLLibName; function SDL_NumJoysticks: LongInt; cdecl; external SDLLibName; -function SDL_JoystickName(idx: LongInt): PChar; cdecl; external SDLLibName; +function SDL_JoystickNameForIndex(idx: LongInt): PChar; cdecl; external SDLLibName; function SDL_JoystickOpen(idx: LongInt): PSDL_Joystick; cdecl; external SDLLibName; function SDL_JoystickOpened(idx: LongInt): LongInt; cdecl; external SDLLibName; function SDL_JoystickIndex(joy: PSDL_Joystick): LongInt; cdecl; external SDLLibName; diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/hwLibrary.pas --- a/hedgewars/hwLibrary.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/hwLibrary.pas Fri Jul 20 20:00:52 2018 -0400 @@ -24,7 +24,7 @@ * and language of choice. * * See also: C declarations on Wikipedia - * http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl + * https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl *) Library hwLibrary; diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/hwengine.pas --- a/hedgewars/hwengine.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/hwengine.pas Fri Jul 20 20:00:52 2018 -0400 @@ -45,12 +45,14 @@ procedure preInitEverything(); procedure initEverything(complete:boolean); procedure freeEverything(complete:boolean); +procedure catchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer); implementation {$ELSE} procedure preInitEverything(); forward; procedure initEverything(complete:boolean); forward; procedure freeEverything(complete:boolean); forward; +procedure catchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer); forward; {$ENDIF} {$IFDEF WIN32} @@ -597,6 +599,28 @@ freeEverything(false); end; +// Write backtrace to console and log when an unhandled exception occurred +procedure catchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer); +var + Message: string; + i: LongInt; +begin + WriteLnToConsole('An unhandled exception occurred at $' + HexStr(Addr) + ':'); + if Obj is exception then + begin + Message := Exception(Obj).ClassName + ': ' + Exception(Obj).Message; + WriteLnToConsole(Message); + end + else + WriteLnToConsole('Exception object ' + Obj.ClassName + ' is not of class Exception.'); + WriteLnToConsole(BackTraceStrFunc(Addr)); + if (FrameCount > 0) then + begin + for i := 0 to FrameCount - 1 do + WriteLnToConsole(BackTraceStrFunc(Frames[i])); + end; +end; + {$IFDEF HWLIBRARY} function RunEngine(argc: LongInt; argv: PPChar): LongInt; cdecl; export; begin @@ -624,17 +648,20 @@ // workaround for pascal's ParamStr and ParamCount init(argc, argv); {$ENDIF} + // Custom procedure for unhandled exceptions; ExceptProc is used by sysutils module + ExceptProc:= @catchUnhandledException; + preInitEverything(); GetParams(); if GameType = gmtLandPreview then GenLandPreview() - else if GameType <> gmtSyntax then + else if (GameType <> gmtBadSyntax) and (GameType <> gmtSyntaxHelp) then Game(); - // return 1 when engine is not called correctly - if GameType = gmtSyntax then + // return error when engine is not called correctly + if GameType = gmtBadSyntax then {$IFDEF PAS2C} exit(HaltUsageError); {$ELSE} diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uAmmos.pas --- a/hedgewars/uAmmos.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uAmmos.pas Fri Jul 20 20:00:52 2018 -0400 @@ -160,7 +160,7 @@ function GetAmmoByNum(num: LongInt): PHHAmmo; begin - if checkFails(num < StoreCnt, 'Invalid store number', true) then + if checkFails(num < StoreCnt, 'Invalid ammo store number', true) then GetAmmoByNum:= nil else GetAmmoByNum:= StoresList[num] diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uConsts.pas --- a/hedgewars/uConsts.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uConsts.pas Fri Jul 20 20:00:52 2018 -0400 @@ -37,6 +37,7 @@ HaltFatalError = 52; HaltStartupError = 53; HaltFatalErrorNoIPC = 54; + HaltVideoRec = 55; // for automatic tests HaltTestSuccess = 0; @@ -171,7 +172,7 @@ cMaxTeams = 8; cMaxHHIndex = 7; - cMaxHHs = 48; + cMaxHHs = cMaxTeams * (cMaxHHIndex+1); cMaxEdgePoints = 32768; diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uCursor.pas --- a/hedgewars/uCursor.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uCursor.pas Fri Jul 20 20:00:52 2018 -0400 @@ -9,7 +9,7 @@ implementation -uses SDLh, uVariables; +uses SDLh, uVariables, uTypes; procedure init; begin @@ -18,6 +18,8 @@ procedure resetPosition; begin + if GameType = gmtRecord then + exit; // Move curser by 1px in case it's already centered. // The game camera in the Alpha for 0.9.23 screwed up if // the game started with the mouse already being centered. @@ -31,13 +33,14 @@ procedure updatePosition; var x, y: LongInt; begin - SDL_GetMouseState(@x, @y); + if GameType <> gmtRecord then + SDL_GetMouseState(@x, @y); if(x <> cScreenWidth div 2) or (y <> cScreenHeight div 2) then begin handlePositionUpdate(x - cScreenWidth div 2, y - cScreenHeight div 2); - if cHasFocus then + if cHasFocus and (GameType <> gmtRecord) then SDL_WarpMouse(cScreenWidth div 2, cScreenHeight div 2); end end; diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uDebug.pas --- a/hedgewars/uDebug.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uDebug.pas Fri Jul 20 20:00:52 2018 -0400 @@ -56,7 +56,7 @@ if not Assert then begin lastConsoleLine:= Msg; - OutError(Msg, false); + OutError(Msg, isFatal); end; allOK:= allOK and (Assert or (not isFatal)); diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uGame.pas --- a/hedgewars/uGame.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uGame.pas Fri Jul 20 20:00:52 2018 -0400 @@ -86,7 +86,7 @@ begin j:= Volume; i:= ChangeVolume(cVolumeDelta); - if isAudioMuted and (j<>i) then + if (not cIsSoundEnabled) or (isAudioMuted and (j<>i)) then AddCaption(trmsg[sidMute], cWhiteColor, capgrpVolume) else if not isAudioMuted then begin diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uIO.pas --- a/hedgewars/uIO.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uIO.pas Fri Jul 20 20:00:52 2018 -0400 @@ -515,7 +515,7 @@ end else if CurrentTeam^.ExtDriven then - OutError('got /put while not being in choose target mode', false) + OutError('Got /put while not being in choose target mode', false) end; procedure initModule; diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uInputHandler.pas --- a/hedgewars/uInputHandler.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uInputHandler.pas Fri Jul 20 20:00:52 2018 -0400 @@ -109,7 +109,7 @@ else begin code:= 0; while (code <= High(CurrentBinds.indices)) and (CurrentBinds.indices[code] <> index) do inc(code); - checkFails(code <= High(CurrentBinds.indices), 'binds registry inconsistency', True); + checkFails(code <= High(CurrentBinds.indices), 'Inconsistency in key binding registry', True); KeyBindToCode:= code; end; end; @@ -333,7 +333,7 @@ procedure RegisterBind(var binds: TBinds; key, value: shortstring); var code: LongInt; begin - checkFails(binds.lastIndex < 255, 'too many binds', true); + checkFails(binds.lastIndex < 255, 'Too many key bindings', true); code:= KeyNameToCode(key); @@ -393,11 +393,40 @@ RegisterBind(DefaultBinds, 'right', '+right'); RegisterBind(DefaultBinds, 'left_shift', '+precise'); + // Default controls for first connected controller + { NOTE: This is provided for convenience so players + don't have to set-up the controller entirely in a new install. + It's not ideal, so players are still encourages to + set up things manually. } + // Essential controls + RegisterBind(DefaultBinds, 'j0h0r', '+right'); + RegisterBind(DefaultBinds, 'j0h0l', '+left'); + RegisterBind(DefaultBinds, 'j0h0u', '+up'); + RegisterBind(DefaultBinds, 'j0h0d', '+down'); + RegisterBind(DefaultBinds, 'j0b0', 'ljump'); + RegisterBind(DefaultBinds, 'j0b1', 'hjump'); + RegisterBind(DefaultBinds, 'j0b2', '+attack'); + RegisterBind(DefaultBinds, 'j0b3', 'ammomenu'); + RegisterBind(DefaultBinds, 'j0b4', '+precise'); + RegisterBind(DefaultBinds, 'j0b5', 'put'); + RegisterBind(DefaultBinds, 'j0b6', 'switch'); + // TODO: Add controller-friendly way to change timer - RegisterBind(DefaultBinds, 'j0a0u', '+left'); - RegisterBind(DefaultBinds, 'j0a0d', '+right'); - RegisterBind(DefaultBinds, 'j0a1u', '+up'); - RegisterBind(DefaultBinds, 'j0a1d', '+down'); + // Cursor movement (also essential) + RegisterBind(DefaultBinds, 'j0h1r', '+cur_r'); + RegisterBind(DefaultBinds, 'j0h1l', '+cur_l'); + RegisterBind(DefaultBinds, 'j0h1d', '+cur_d'); + RegisterBind(DefaultBinds, 'j0h1u', '+cur_u'); + + RegisterBind(DefaultBinds, 'j0a0u', '+cur_r'); + RegisterBind(DefaultBinds, 'j0a0d', '+cur_l'); + RegisterBind(DefaultBinds, 'j0a1u', '+cur_d'); + RegisterBind(DefaultBinds, 'j0a1d', '+cur_u'); + + // Additional controls + RegisterBind(DefaultBinds, 'j0b7', 'findhh'); + RegisterBind(DefaultBinds, 'j0b8', '+mission'); + for i:= 1 to 10 do RegisterBind(DefaultBinds, 'f'+IntToStr(i), 'slot '+char(48+i)); for i:= 1 to 5 do RegisterBind(DefaultBinds, IntToStr(i), 'timer '+IntToStr(i)); @@ -514,10 +543,10 @@ begin for j:= 0 to pred(ControllerNumControllers) do begin - WriteLnToConsole('Using game controller: ' + shortstring(SDL_JoystickName(j))); + WriteLnToConsole('Game controller no. ' + IntToStr(j) + ', name "' + shortstring(SDL_JoystickNameForIndex(j)) + '":'); Controller[j]:= SDL_JoystickOpen(j); if Controller[j] = nil then - WriteLnToConsole('* Failed to open game controller!') + WriteLnToConsole('* Failed to open game controller no. ' + IntToStr(j) + '!') else begin ControllerNumAxes[j]:= SDL_JoystickNumAxes(Controller[j]); diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uLand.pas --- a/hedgewars/uLand.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uLand.pas Fri Jul 20 20:00:52 2018 -0400 @@ -314,7 +314,7 @@ end else getRandom(1); case cTemplateFilter of - 0: OutError('Ask unC0Rr about what you did wrong', true); + 0: OutError('Error selecting TemplateFilter. Ask unC0Rr about what you did wrong', true); 1: SelectTemplate:= SmallTemplates[getrandom(TemplateCounts[cTemplateFilter])]; 2: SelectTemplate:= MediumTemplates[getrandom(TemplateCounts[cTemplateFilter])]; 3: SelectTemplate:= LargeTemplates[getrandom(TemplateCounts[cTemplateFilter])]; @@ -974,7 +974,7 @@ if digest = '' then digest:= s else - checkFails(s = digest, 'Different map or critical resources loaded, sorry', true); + checkFails(s = digest, 'Loaded map or other critical resource does not match across all players', true); end; procedure chSendLandDigest(var s: shortstring); diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uLandObjects.pas --- a/hedgewars/uLandObjects.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uLandObjects.pas Fri Jul 20 20:00:52 2018 -0400 @@ -647,9 +647,9 @@ procedure CheckRect(Width, Height, x, y, w, h: LongWord); begin if (x + w > Width) then - OutError('Object''s rectangle exceeds image: x + w (' + inttostr(x) + ' + ' + inttostr(w) + ') > Width (' + inttostr(Width) + ')', true); + OutError('Broken theme. Object''s rectangle exceeds image: x + w (' + inttostr(x) + ' + ' + inttostr(w) + ') > Width (' + inttostr(Width) + ')', true); if (y + h > Height) then - OutError('Object''s rectangle exceeds image: y + h (' + inttostr(y) + ' + ' + inttostr(h) + ') > Height (' + inttostr(Height) + ')', true); + OutError('Broken theme. Object''s rectangle exceeds image: y + h (' + inttostr(y) + ' + ' + inttostr(h) + ') > Height (' + inttostr(Height) + ')', true); end; procedure ReadRect(var rect: TSDL_Rect; var s: ShortString); @@ -901,7 +901,7 @@ Maxcnt:= StrToInt(Trim(Copy(s, 1, Pred(i)))); Delete(s, 1, i); if (Maxcnt < 1) or (Maxcnt > MAXTHEMEOBJECTS) then - OutError('Object''s max count should be between 1 and '+ inttostr(MAXTHEMEOBJECTS) +' (it was '+ inttostr(Maxcnt) +').', true); + OutError('Broken theme. Object''s max. count should be between 1 and '+ inttostr(MAXTHEMEOBJECTS) +' (it was '+ inttostr(Maxcnt) +').', true); ChecksumLandObjectImage(Surf); ChecksumLandObjectImage(Mask); @@ -920,7 +920,7 @@ end; if inrectcnt > MAXOBJECTRECTS then - OutError('Object''s inland rectangle count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(inrectcnt) +').', true); + OutError('Broken theme. Object''s inland rectangle count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(inrectcnt) +').', true); for ii:= 0 to Pred(inrectcnt) do ReadRect(inland[ii], s); @@ -930,7 +930,7 @@ Delete(s, 1, i); if outrectcnt > MAXOBJECTRECTS then - OutError('Object''s outland rectangle count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(outrectcnt) +').', true); + OutError('Broken theme. Object''s outland rectangle count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(outrectcnt) +').', true); for ii:= 0 to Pred(outrectcnt) do ReadRect(outland[ii], s); @@ -944,13 +944,13 @@ if ThemeObjects.objs[ii].Name = nameRef then with ThemeObjects.objs[ii] do begin if anchorcnt <> 0 then - OutError('Duplicate anchors declaration for ' + nameRef, true); + OutError('Broken theme. Duplicate anchors declaration for object ' + nameRef, true); Delete(s, 1, i); i:= Pos(',', s); anchorcnt:= StrToInt(Trim(Copy(s, 1, Pred(i)))); Delete(s, 1, i); if anchorcnt > MAXOBJECTRECTS then - OutError('Object''s anchor rectangle count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(anchorcnt) +').', true); + OutError('Broken theme. Object''s anchor rectangle count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(anchorcnt) +').', true); for t:= 0 to Pred(anchorcnt) do ReadRect(anchors[t], s); break @@ -964,13 +964,13 @@ if ThemeObjects.objs[ii].Name = nameRef then with ThemeObjects.objs[ii] do begin if overlaycnt <> 0 then - OutError('Duplicate overlays declaration for ' + nameRef, true); + OutError('Broken theme. Duplicate overlays declaration for object ' + nameRef, true); Delete(s, 1, i); i:= Pos(',', s); overlaycnt:= StrToInt(Trim(Copy(s, 1, Pred(i)))); Delete(s, 1, i); if overlaycnt > MAXOBJECTRECTS then - OutError('Object''s overlay count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(overlaycnt) +').', true); + OutError('Broken theme. Object''s overlay count should be no more than '+ inttostr(MAXOBJECTRECTS) +' (it was '+ inttostr(overlaycnt) +').', true); for t:= 0 to Pred(overlaycnt) do ReadOverlay(overlays[t], s); break diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uMatrix.pas --- a/hedgewars/uMatrix.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uMatrix.pas Fri Jul 20 20:00:52 2018 -0400 @@ -260,7 +260,7 @@ write(Result[i, j]); writeln; end; - checkFails(false, 'error in matrix multiplication?!', true); + checkFails(false, 'Error in matrix multiplication?!', true); end; {$ENDIF} diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uPhysFSLayer.pas --- a/hedgewars/uPhysFSLayer.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uPhysFSLayer.pas Fri Jul 20 20:00:52 2018 -0400 @@ -202,7 +202,7 @@ {$ENDIF} begin {$IFDEF HWLIBRARY} - //TODO: http://icculus.org/pipermail/physfs/2011-August/001006.html + //TODO: https://icculus.org/pipermail/physfs/2011-August/001006.html cPhysfsId:= shortstring(GetCurrentDir()) + {$IFDEF DARWIN}{$IFNDEF IPHONEOS}'/Hedgewars.app/Contents/MacOS/' + {$ENDIF}{$ENDIF} ' hedgewars'; {$ELSE} cPhysfsId:= ParamStr(0); diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uRender.pas --- a/hedgewars/uRender.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uRender.pas Fri Jul 20 20:00:52 2018 -0400 @@ -527,7 +527,10 @@ {$IFNDEF PAS2C} if not Load_GL_VERSION_2_0 then - halt; + begin + WriteLnToConsole('Load_GL_VERSION_2_0 returned false!'); + halt(HaltStartupError); + end; {$ENDIF} shaderWater:= CompileProgram('water'); @@ -718,7 +721,7 @@ end; procedure openglRotatef(RotX, RotY, RotZ: GLfloat; dir: LongInt); inline; -{ workaround for pascal bug http://bugs.freepascal.org/view.php?id=27222 } +{ workaround for pascal bug https://bugs.freepascal.org/view.php?id=27222 } var tmpdir: LongInt; begin tmpdir:=dir; diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uRenderUtils.pas --- a/hedgewars/uRenderUtils.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uRenderUtils.pas Fri Jul 20 20:00:52 2018 -0400 @@ -46,7 +46,7 @@ begin r:= rect^; if Clear then - SDL_FillRect(Surface, @r, 0); + SDL_FillRect(Surface, @r, SDL_MapRGB(Surface^.format, 0, 0, 0)); BorderColor:= SDL_MapRGB(Surface^.format, BorderColor shr 16, BorderColor shr 8, BorderColor and $FF); FillColor:= SDL_MapRGB(Surface^.format, FillColor shr 16, FillColor shr 8, FillColor and $FF); @@ -554,7 +554,7 @@ rect.h:= textHeight + cornerHeight * 2 - edgeHeight * 2; i:= rect.w; j:= rect.h; - SDL_FillRect(finalSurface, @rect, cWhiteColor); + SDL_FillRect(finalSurface, @rect, SDL_MapRGB(finalSurface^.format, cWhiteColor shr 16, cWhiteColor shr 8, cWhiteColor and $FF)); pos:= 1; line:= 0; while GetNextSpeechLine(s, #1, pos, substr) do diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uSound.pas --- a/hedgewars/uSound.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uSound.pas Fri Jul 20 20:00:52 2018 -0400 @@ -339,7 +339,11 @@ var success: boolean; begin if not (isSoundEnabled or isMusicEnabled) then + begin + isAudioMuted:= true; + cInitVolume:= 0; exit; + end; WriteToConsole('Init sound...'); success:= SDL_InitSubSystem(SDL_INIT_AUDIO) = 0; @@ -357,12 +361,17 @@ WriteLnToConsole(msgFailed); isSoundEnabled:= false; isMusicEnabled:= false; + isAudioMuted:= true; + cInitVolume:= 0; end; WriteToConsole('Init SDL_mixer... '); if SDLCheck(Mix_Init(MIX_INIT_OGG) <> 0, 'Mix_Init', true) then exit; WriteLnToConsole(msgOK); + // from uVariables to be used by other modules + cIsSoundEnabled:= true; + Mix_AllocateChannels(Succ(chanTPU)); previousVolume:= cInitVolume; ChangeVolume(cInitVolume); diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uStore.pas --- a/hedgewars/uStore.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uStore.pas Fri Jul 20 20:00:52 2018 -0400 @@ -288,7 +288,7 @@ if (month = 4) and (md = 1) then begin AprilOne:= true; - Hat := 'fr_tomato'; // avoid promoting violence to hedgehogs. see http://hedgewars.org/node/5818 + Hat := 'fr_tomato'; // avoid promoting violence to hedgehogs. see https://hedgewars.org/node/5818 end; if Hat <> 'NoHat' then @@ -829,7 +829,7 @@ {$ENDIF} end; - if checkFails((ProgrTex <> nil) and (LoadingText <> nil), 'Error - Progress or Loading Texture is nil!', true) then exit; + if checkFails((ProgrTex <> nil) and (LoadingText <> nil), 'Error - Progress or Loading texture is nil!', true) then exit; RenderClear(); if Step < numsquares then @@ -975,7 +975,7 @@ r.y:= cFontBorder + 4; r.w:= 32; r.h:= 32; -SDL_FillRect(tmpsurf, @r, $ff000000); +SDL_FillRect(tmpsurf, @r, SDL_MapRGB(tmpsurf^.format, 0, 0, 0)); SDL_UpperBlit(iconsurf, iconrect, tmpsurf, @r); RenderHelpWindow:= Surface2Tex(tmpsurf, true); diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uTeams.pas --- a/hedgewars/uTeams.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uTeams.pas Fri Jul 20 20:00:52 2018 -0400 @@ -148,7 +148,7 @@ PrevHH, PrevTeam : LongWord; begin TargetPoint.X:= NoPointX; -if checkFails(CurrentTeam <> nil, 'nil Team', true) then exit; +if checkFails(CurrentTeam <> nil, 'Team is nil!', true) then exit; with CurrentHedgehog^ do if (PreviousTeam <> nil) and PlacingHogs and Unplaced then begin diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uTypes.pas --- a/hedgewars/uTypes.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uTypes.pas Fri Jul 20 20:00:52 2018 -0400 @@ -39,7 +39,7 @@ TGameState = (gsLandGen, gsStart, gsGame, gsConfirm, gsExit, gsSuspend); // Game types that help determining what the engine is actually supposed to do - TGameType = (gmtLocal, gmtDemo, gmtNet, gmtSave, gmtLandPreview, gmtSyntax, gmtRecord); + TGameType = (gmtLocal, gmtDemo, gmtNet, gmtSave, gmtLandPreview, gmtBadSyntax, gmtRecord, gmtSyntaxHelp); // Different files are stored in different folders, this enumeration is used to tell which folder to use TPathType = (ptNone, ptData, ptGraphics, ptThemes, ptCurrTheme, ptConfig, ptTeams, ptMaps, diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uVariables.pas --- a/hedgewars/uVariables.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uVariables.pas Fri Jul 20 20:00:52 2018 -0400 @@ -168,6 +168,7 @@ cScriptName : shortstring; cScriptParam : shortstring; cSeed : shortstring; + cIsSoundEnabled : boolean; // If the sound system is enabled cVolumeDelta : LongInt; cMuteToggle : boolean; // Mute toggle requested cHasFocus : boolean; @@ -2852,6 +2853,7 @@ fastScrolling := false; autoCameraOn := true; cSeed := ''; + cIsSoundEnabled := false; cVolumeDelta := 0; cMuteToggle := false; cHasFocus := true; diff -r 80db7232b4b5 -r 282218ab1b28 hedgewars/uVideoRec.pas --- a/hedgewars/uVideoRec.pas Sat Jul 21 02:19:32 2018 +0300 +++ b/hedgewars/uVideoRec.pas Fri Jul 20 20:00:52 2018 -0400 @@ -48,7 +48,7 @@ procedure freeModule; implementation -uses uVariables, GLunit, SDLh, SysUtils, uUtils, uIO, uMisc, uTypes, uDebug; +uses uVariables, GLunit, SDLh, SysUtils, uUtils, uIO, uMisc, uConsts, uTypes, uDebug; type TAddFileLogRaw = procedure (s: pchar); cdecl; const AvwrapperLibName = 'libavwrapper'; @@ -73,7 +73,8 @@ numPixels: LongWord; startTime, numFrames, curTime, progress, maxProgress: LongWord; soundFilePath: shortstring; - thumbnailSaved : Boolean; + thumbnailSaved: boolean; + recordAudio: boolean; function BeginVideoRecording: Boolean; var filename, desc: shortstring; @@ -113,7 +114,12 @@ desc:= desc + 'prefix[' + RecPrefix + ']prefix'; filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix; - soundFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw'; + + recordAudio:= (cAudioCodec <> 'no'); + if recordAudio then + soundFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw' + else + soundFilePath:= ''; if checkFails(AVWrapper_Init(@AddFileLogRaw , PChar(ansistring(filename)) @@ -147,9 +153,20 @@ FreeMem(RGB_Buffer, 4*numPixels); Close(cameraFile); if AVWrapper_Close() < 0 then - halt(-1); - Erase(cameraFile); - DeleteFile(soundFilePath); + begin + AddFileLog('AVWrapper_Close() has failed.'); + halt(HaltVideoRec); + end; +{$IOCHECKS OFF} + // Provoke IOResult to be set + FileSize(cameraFile); + if IOResult = 0 then + Erase(cameraFile) + else + AddFileLog('Warning: Tried to delete the cameraFile but it was already deleted'); +{$IOCHECKS ON} + if recordAudio and FileExists(soundFilePath) then + DeleteFile(soundFilePath); SendIPC(_S'v'); // inform frontend that we finished end; @@ -160,7 +177,10 @@ glReadPixels(0, 0, cScreenWidth, cScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, RGB_Buffer); if AVWrapper_WriteFrame(RGB_Buffer) < 0 then - halt(-1); + begin + AddFileLog('AVWrapper_WriteFrame(RGB_Buffer) has failed.'); + halt(HaltVideoRec); + end; // inform frontend that we have encoded new frame s[0]:= #3; @@ -268,43 +288,49 @@ CopyFile(recordFileName, UserPathPrefix + '/VideoTemp/' + RecPrefix + '.hwd'); end; - Mix_QuerySpec(@frequency, @format, @channels); - AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels)); - if format <> $8010 then - begin - // TODO: support any audio format - AddFileLog('Error: Unexpected audio format ' + IntToStr(format)); - exit; - end; + if cIsSoundEnabled then + begin + Mix_QuerySpec(@frequency, @format, @channels); + AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels)); + if format <> $8010 then + begin + // TODO: support any audio format + AddFileLog('Error: Unexpected audio format ' + IntToStr(format)); + exit; + end; {$IOCHECKS OFF} - // create sound file - filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw'; - Assign(audioFile, filename); - Rewrite(audioFile, 1); - if IOResult <> 0 then - begin - AddFileLog('Error: Could not write to ' + filename); - exit; - end; + // create sound file + filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw'; + Assign(audioFile, filename); + Rewrite(audioFile, 1); + if IOResult <> 0 then + begin + AddFileLog('Error: Could not write to ' + filename); + exit; + end; + end; // create file with camera positions filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtout'; Assign(cameraFile, filename); Rewrite(cameraFile); if IOResult <> 0 then - begin + begin AddFileLog('Error: Could not write to ' + filename); exit; - end; + end; - // save audio parameters in sound file - BlockWrite(audioFile, frequency, 4); - BlockWrite(audioFile, channels, 4); + if cIsSoundEnabled then + begin + // save audio parameters in sound file + BlockWrite(audioFile, frequency, 4); + BlockWrite(audioFile, channels, 4); {$IOCHECKS ON} - // register callback for actual audio recording - Mix_SetPostMix(@RecordPostMix, nil); + // register callback for actual audio recording + Mix_SetPostMix(@RecordPostMix, nil); + end; startTime:= SDL_GetTicks(); flagPrerecording:= true; @@ -315,12 +341,18 @@ AddFileLog('StopPreRecording'); flagPrerecording:= false; - // call SDL_LockAudio because RecordPostMix may be executing right now - SDL_LockAudio(); - Close(audioFile); + if cIsSoundEnabled then + begin + // call SDL_LockAudio because RecordPostMix may be executing right now + SDL_LockAudio(); + Close(audioFile); + end; Close(cameraFile); - Mix_SetPostMix(nil, nil); - SDL_UnlockAudio(); + if cIsSoundEnabled then + begin + Mix_SetPostMix(nil, nil); + SDL_UnlockAudio(); + end; if not thumbnailSaved then SaveThumbnail(); diff -r 80db7232b4b5 -r 282218ab1b28 misc/hats_js_anim.xhtml --- a/misc/hats_js_anim.xhtml Sat Jul 21 02:19:32 2018 +0300 +++ b/misc/hats_js_anim.xhtml Fri Jul 20 20:00:52 2018 -0400 @@ -45,7 +45,7 @@