merge 1.0.0 beta 1 into ui-scaling branch ui-scaling
authorsheepluva
Wed, 31 Jul 2019 23:14:27 +0200
branchui-scaling
changeset 15283 c4fd2813b127
parent 13390 0135e64c6c66 (current diff)
parent 15279 7ab5cf405686 (diff)
child 15663 d92eeb468dad
merge 1.0.0 beta 1 into ui-scaling branch
ChangeLog.txt
QTfrontend/game.cpp
QTfrontend/gameuiconfig.cpp
QTfrontend/gameuiconfig.h
QTfrontend/net/recorder.cpp
QTfrontend/res/campaign/A_Classic_Fairytale/backstab.png
QTfrontend/res/campaign/A_Classic_Fairytale/dragon.png
QTfrontend/res/campaign/A_Classic_Fairytale/enemy.png
QTfrontend/res/campaign/A_Classic_Fairytale/epil.png
QTfrontend/res/campaign/A_Classic_Fairytale/family.png
QTfrontend/res/campaign/A_Classic_Fairytale/first_blood.png
QTfrontend/res/campaign/A_Classic_Fairytale/journey.png
QTfrontend/res/campaign/A_Classic_Fairytale/queen.png
QTfrontend/res/campaign/A_Classic_Fairytale/shadow.png
QTfrontend/res/campaign/A_Classic_Fairytale/united.png
QTfrontend/res/campaign/A_Space_Adventure/cosmos.png
QTfrontend/res/campaign/A_Space_Adventure/death01.png
QTfrontend/res/campaign/A_Space_Adventure/death02.png
QTfrontend/res/campaign/A_Space_Adventure/desert01.png
QTfrontend/res/campaign/A_Space_Adventure/desert02.png
QTfrontend/res/campaign/A_Space_Adventure/desert03.png
QTfrontend/res/campaign/A_Space_Adventure/final.png
QTfrontend/res/campaign/A_Space_Adventure/fruit01.png
QTfrontend/res/campaign/A_Space_Adventure/fruit02.png
QTfrontend/res/campaign/A_Space_Adventure/fruit03.png
QTfrontend/res/campaign/A_Space_Adventure/ice01.png
QTfrontend/res/campaign/A_Space_Adventure/ice02.png
QTfrontend/res/campaign/A_Space_Adventure/moon01.png
QTfrontend/res/campaign/A_Space_Adventure/moon02.png
QTfrontend/res/html/about.html
QTfrontend/ui/page/pageoptions.cpp
QTfrontend/ui/page/pageoptions.h
gameServer2/Cargo.toml
gameServer2/src/main.rs
gameServer2/src/protocol/messages.rs
gameServer2/src/protocol/mod.rs
gameServer2/src/protocol/parser.rs
gameServer2/src/server/actions.rs
gameServer2/src/server/client.rs
gameServer2/src/server/coretypes.rs
gameServer2/src/server/handlers/inroom.rs
gameServer2/src/server/handlers/lobby.rs
gameServer2/src/server/handlers/loggingin.rs
gameServer2/src/server/handlers/mod.rs
gameServer2/src/server/mod.rs
gameServer2/src/server/network.rs
gameServer2/src/server/room.rs
gameServer2/src/server/server.rs
gameServer2/src/utils.rs
hedgewars/ArgParsers.pas
hedgewars/uChat.pas
hedgewars/uConsts.pas
hedgewars/uStore.pas
hedgewars/uTypes.pas
hedgewars/uVariables.pas
misc/libphysfs/Android.mk
misc/libphysfs/CMakeLists.txt
misc/libphysfs/Xcode/Physfs.xcodeproj/project.pbxproj
misc/libphysfs/Xcode/Physfs_Prefix.pch
misc/libphysfs/archiver_dir.c
misc/libphysfs/archiver_grp.c
misc/libphysfs/archiver_hog.c
misc/libphysfs/archiver_iso9660.c
misc/libphysfs/archiver_lzma.c
misc/libphysfs/archiver_mvl.c
misc/libphysfs/archiver_qpak.c
misc/libphysfs/archiver_slb.c
misc/libphysfs/archiver_unpacked.c
misc/libphysfs/archiver_wad.c
misc/libphysfs/archiver_zip.c
misc/libphysfs/lzma/7zC.txt
misc/libphysfs/lzma/7zFormat.txt
misc/libphysfs/lzma/C/7zCrc.c
misc/libphysfs/lzma/C/7zCrc.h
misc/libphysfs/lzma/C/7zCrcT8.c
misc/libphysfs/lzma/C/Alloc.c
misc/libphysfs/lzma/C/Alloc.h
misc/libphysfs/lzma/C/Archive/7z/7zAlloc.c
misc/libphysfs/lzma/C/Archive/7z/7zAlloc.h
misc/libphysfs/lzma/C/Archive/7z/7zBuffer.c
misc/libphysfs/lzma/C/Archive/7z/7zBuffer.h
misc/libphysfs/lzma/C/Archive/7z/7zDecode.c
misc/libphysfs/lzma/C/Archive/7z/7zDecode.h
misc/libphysfs/lzma/C/Archive/7z/7zExtract.c
misc/libphysfs/lzma/C/Archive/7z/7zExtract.h
misc/libphysfs/lzma/C/Archive/7z/7zHeader.c
misc/libphysfs/lzma/C/Archive/7z/7zHeader.h
misc/libphysfs/lzma/C/Archive/7z/7zIn.c
misc/libphysfs/lzma/C/Archive/7z/7zIn.h
misc/libphysfs/lzma/C/Archive/7z/7zItem.c
misc/libphysfs/lzma/C/Archive/7z/7zItem.h
misc/libphysfs/lzma/C/Archive/7z/7zMain.c
misc/libphysfs/lzma/C/Archive/7z/7zMethodID.c
misc/libphysfs/lzma/C/Archive/7z/7zMethodID.h
misc/libphysfs/lzma/C/Archive/7z/7z_C.dsp
misc/libphysfs/lzma/C/Archive/7z/7z_C.dsw
misc/libphysfs/lzma/C/Archive/7z/makefile
misc/libphysfs/lzma/C/Archive/7z/makefile.gcc
misc/libphysfs/lzma/C/Compress/Branch/BranchARM.c
misc/libphysfs/lzma/C/Compress/Branch/BranchARM.h
misc/libphysfs/lzma/C/Compress/Branch/BranchARMThumb.c
misc/libphysfs/lzma/C/Compress/Branch/BranchARMThumb.h
misc/libphysfs/lzma/C/Compress/Branch/BranchIA64.c
misc/libphysfs/lzma/C/Compress/Branch/BranchIA64.h
misc/libphysfs/lzma/C/Compress/Branch/BranchPPC.c
misc/libphysfs/lzma/C/Compress/Branch/BranchPPC.h
misc/libphysfs/lzma/C/Compress/Branch/BranchSPARC.c
misc/libphysfs/lzma/C/Compress/Branch/BranchSPARC.h
misc/libphysfs/lzma/C/Compress/Branch/BranchTypes.h
misc/libphysfs/lzma/C/Compress/Branch/BranchX86.c
misc/libphysfs/lzma/C/Compress/Branch/BranchX86.h
misc/libphysfs/lzma/C/Compress/Branch/BranchX86_2.c
misc/libphysfs/lzma/C/Compress/Branch/BranchX86_2.h
misc/libphysfs/lzma/C/Compress/Huffman/HuffmanEncode.c
misc/libphysfs/lzma/C/Compress/Huffman/HuffmanEncode.h
misc/libphysfs/lzma/C/Compress/Lz/LzHash.h
misc/libphysfs/lzma/C/Compress/Lz/MatchFinder.c
misc/libphysfs/lzma/C/Compress/Lz/MatchFinder.h
misc/libphysfs/lzma/C/Compress/Lz/MatchFinderMt.c
misc/libphysfs/lzma/C/Compress/Lz/MatchFinderMt.h
misc/libphysfs/lzma/C/Compress/Lzma/LzmaDecode.c
misc/libphysfs/lzma/C/Compress/Lzma/LzmaDecode.h
misc/libphysfs/lzma/C/Compress/Lzma/LzmaDecodeSize.c
misc/libphysfs/lzma/C/Compress/Lzma/LzmaStateDecode.c
misc/libphysfs/lzma/C/Compress/Lzma/LzmaStateDecode.h
misc/libphysfs/lzma/C/Compress/Lzma/LzmaStateTest.c
misc/libphysfs/lzma/C/Compress/Lzma/LzmaTest.c
misc/libphysfs/lzma/C/Compress/Lzma/LzmaTypes.h
misc/libphysfs/lzma/C/CpuArch.h
misc/libphysfs/lzma/C/IStream.h
misc/libphysfs/lzma/C/Sort.c
misc/libphysfs/lzma/C/Sort.h
misc/libphysfs/lzma/C/Threads.c
misc/libphysfs/lzma/C/Threads.h
misc/libphysfs/lzma/C/Types.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7z.ico
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zCompressionMode.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zDecode.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zDecode.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zEncode.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zEncode.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zExtract.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zFolderInStream.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zFolderOutStream.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHandler.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHandler.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHeader.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHeader.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zIn.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zIn.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zItem.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zOut.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zOut.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zProperties.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zProperties.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zRegister.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zSpecStream.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zUpdate.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zUpdate.h
misc/libphysfs/lzma/CPP/7zip/Archive/7z/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/7z/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Archive/Archive.def
misc/libphysfs/lzma/CPP/7zip/Archive/Archive2.def
misc/libphysfs/lzma/CPP/7zip/Archive/ArchiveExports.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/CoderMixer2.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/CoderMixer2MT.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/CrossThreadProgress.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/DummyOutStream.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/HandlerOut.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/HandlerOut.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/InStreamWithCRC.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/ItemNameUtils.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/MultiStream.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/MultiStream.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/OutStreamWithCRC.h
misc/libphysfs/lzma/CPP/7zip/Archive/Common/ParseProperties.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/Common/ParseProperties.h
misc/libphysfs/lzma/CPP/7zip/Archive/DllExports2.cpp
misc/libphysfs/lzma/CPP/7zip/Archive/IArchive.h
misc/libphysfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsp
misc/libphysfs/lzma/CPP/7zip/Bundles/Alone7z/Alone.dsw
misc/libphysfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Bundles/Alone7z/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Bundles/Alone7z/makefile
misc/libphysfs/lzma/CPP/7zip/Bundles/Alone7z/resource.rc
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zExtractR/makefile
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zExtractR/resource.rc
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zR/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zR/makefile
misc/libphysfs/lzma/CPP/7zip/Bundles/Format7zR/resource.rc
misc/libphysfs/lzma/CPP/7zip/Common/CreateCoder.cpp
misc/libphysfs/lzma/CPP/7zip/Common/CreateCoder.h
misc/libphysfs/lzma/CPP/7zip/Common/FilePathAutoRename.cpp
misc/libphysfs/lzma/CPP/7zip/Common/FilePathAutoRename.h
misc/libphysfs/lzma/CPP/7zip/Common/FileStreams.cpp
misc/libphysfs/lzma/CPP/7zip/Common/FileStreams.h
misc/libphysfs/lzma/CPP/7zip/Common/FilterCoder.cpp
misc/libphysfs/lzma/CPP/7zip/Common/FilterCoder.h
misc/libphysfs/lzma/CPP/7zip/Common/InBuffer.cpp
misc/libphysfs/lzma/CPP/7zip/Common/InBuffer.h
misc/libphysfs/lzma/CPP/7zip/Common/InOutTempBuffer.cpp
misc/libphysfs/lzma/CPP/7zip/Common/InOutTempBuffer.h
misc/libphysfs/lzma/CPP/7zip/Common/LimitedStreams.cpp
misc/libphysfs/lzma/CPP/7zip/Common/LimitedStreams.h
misc/libphysfs/lzma/CPP/7zip/Common/LockedStream.cpp
misc/libphysfs/lzma/CPP/7zip/Common/LockedStream.h
misc/libphysfs/lzma/CPP/7zip/Common/MethodId.cpp
misc/libphysfs/lzma/CPP/7zip/Common/MethodId.h
misc/libphysfs/lzma/CPP/7zip/Common/MethodProps.cpp
misc/libphysfs/lzma/CPP/7zip/Common/MethodProps.h
misc/libphysfs/lzma/CPP/7zip/Common/OffsetStream.cpp
misc/libphysfs/lzma/CPP/7zip/Common/OffsetStream.h
misc/libphysfs/lzma/CPP/7zip/Common/OutBuffer.cpp
misc/libphysfs/lzma/CPP/7zip/Common/OutBuffer.h
misc/libphysfs/lzma/CPP/7zip/Common/ProgressUtils.cpp
misc/libphysfs/lzma/CPP/7zip/Common/ProgressUtils.h
misc/libphysfs/lzma/CPP/7zip/Common/RegisterArc.h
misc/libphysfs/lzma/CPP/7zip/Common/RegisterCodec.h
misc/libphysfs/lzma/CPP/7zip/Common/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Common/StreamBinder.cpp
misc/libphysfs/lzma/CPP/7zip/Common/StreamBinder.h
misc/libphysfs/lzma/CPP/7zip/Common/StreamObjects.cpp
misc/libphysfs/lzma/CPP/7zip/Common/StreamObjects.h
misc/libphysfs/lzma/CPP/7zip/Common/StreamUtils.cpp
misc/libphysfs/lzma/CPP/7zip/Common/StreamUtils.h
misc/libphysfs/lzma/CPP/7zip/Common/VirtThread.cpp
misc/libphysfs/lzma/CPP/7zip/Common/VirtThread.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/ARM.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/ARM.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/ARMThumb.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/BCJ2Register.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/BCJRegister.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/BranchCoder.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/BranchRegister.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/IA64.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/IA64.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/PPC.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/PPC.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/SPARC.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/SPARC.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/x86.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/x86.h
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/x86_2.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Branch/x86_2.h
misc/libphysfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwap.h
misc/libphysfs/lzma/CPP/7zip/Compress/ByteSwap/ByteSwapRegister.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/ByteSwap/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Compress/CodecExports.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Copy/CopyCoder.h
misc/libphysfs/lzma/CPP/7zip/Compress/Copy/CopyRegister.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Copy/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/Copy/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZ/LZOutWindow.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZ/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/LZMA.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/LZMAEncoder.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/LZMARegister.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsw
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaAlone.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBench.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.c
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRamDecode.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile
misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/makefile.gcc
misc/libphysfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoder.h
misc/libphysfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.cpp
misc/libphysfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBit.h
misc/libphysfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderBitTree.h
misc/libphysfs/lzma/CPP/7zip/Compress/RangeCoder/RangeCoderOpt.h
misc/libphysfs/lzma/CPP/7zip/Compress/RangeCoder/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/ICoder.h
misc/libphysfs/lzma/CPP/7zip/IDecl.h
misc/libphysfs/lzma/CPP/7zip/IPassword.h
misc/libphysfs/lzma/CPP/7zip/IProgress.h
misc/libphysfs/lzma/CPP/7zip/IStream.h
misc/libphysfs/lzma/CPP/7zip/MyVersion.h
misc/libphysfs/lzma/CPP/7zip/MyVersionInfo.rc
misc/libphysfs/lzma/CPP/7zip/PropID.h
misc/libphysfs/lzma/CPP/7zip/UI/Client7z/Client7z.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsp
misc/libphysfs/lzma/CPP/7zip/UI/Client7z/Client7z.dsw
misc/libphysfs/lzma/CPP/7zip/UI/Client7z/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Client7z/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/UI/Client7z/makefile
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveName.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveName.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/DefaultName.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/DefaultName.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/DirItem.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/EnumDirItems.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/ExitCode.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/Extract.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/Extract.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/ExtractMode.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/LoadCodecs.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/OpenArchive.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/OpenArchive.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/PropIDUtils.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/Property.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/SetProperties.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/SetProperties.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/SortUtils.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/SortUtils.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/TempFiles.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/TempFiles.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/Update.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/Update.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdateAction.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdateAction.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdateCallback.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdatePair.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdatePair.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/UpdateProduce.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/WorkDir.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Common/WorkDir.h
misc/libphysfs/lzma/CPP/7zip/UI/Common/ZipRegistry.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/ConsoleClose.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/ConsoleClose.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/ExtractCallbackConsole.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/List.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/List.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/Main.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/MainAr.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/OpenCallbackConsole.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/PercentPrinter.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/PercentPrinter.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/StdAfx.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/StdAfx.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/UpdateCallbackConsole.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/UserInputUtils.cpp
misc/libphysfs/lzma/CPP/7zip/UI/Console/UserInputUtils.h
misc/libphysfs/lzma/CPP/7zip/UI/Console/afxres.h
misc/libphysfs/lzma/CPP/Build.mak
misc/libphysfs/lzma/CPP/Common/AutoPtr.h
misc/libphysfs/lzma/CPP/Common/Buffer.h
misc/libphysfs/lzma/CPP/Common/CRC.cpp
misc/libphysfs/lzma/CPP/Common/C_FileIO.cpp
misc/libphysfs/lzma/CPP/Common/C_FileIO.h
misc/libphysfs/lzma/CPP/Common/ComTry.h
misc/libphysfs/lzma/CPP/Common/CommandLineParser.cpp
misc/libphysfs/lzma/CPP/Common/CommandLineParser.h
misc/libphysfs/lzma/CPP/Common/Defs.h
misc/libphysfs/lzma/CPP/Common/DynamicBuffer.h
misc/libphysfs/lzma/CPP/Common/IntToString.cpp
misc/libphysfs/lzma/CPP/Common/IntToString.h
misc/libphysfs/lzma/CPP/Common/ListFileUtils.cpp
misc/libphysfs/lzma/CPP/Common/ListFileUtils.h
misc/libphysfs/lzma/CPP/Common/MyCom.h
misc/libphysfs/lzma/CPP/Common/MyException.h
misc/libphysfs/lzma/CPP/Common/MyGuidDef.h
misc/libphysfs/lzma/CPP/Common/MyInitGuid.h
misc/libphysfs/lzma/CPP/Common/MyString.cpp
misc/libphysfs/lzma/CPP/Common/MyString.h
misc/libphysfs/lzma/CPP/Common/MyUnknown.h
misc/libphysfs/lzma/CPP/Common/MyVector.cpp
misc/libphysfs/lzma/CPP/Common/MyVector.h
misc/libphysfs/lzma/CPP/Common/MyWindows.h
misc/libphysfs/lzma/CPP/Common/NewHandler.cpp
misc/libphysfs/lzma/CPP/Common/NewHandler.h
misc/libphysfs/lzma/CPP/Common/StdAfx.h
misc/libphysfs/lzma/CPP/Common/StdInStream.cpp
misc/libphysfs/lzma/CPP/Common/StdInStream.h
misc/libphysfs/lzma/CPP/Common/StdOutStream.cpp
misc/libphysfs/lzma/CPP/Common/StdOutStream.h
misc/libphysfs/lzma/CPP/Common/StringConvert.cpp
misc/libphysfs/lzma/CPP/Common/StringConvert.h
misc/libphysfs/lzma/CPP/Common/StringToInt.cpp
misc/libphysfs/lzma/CPP/Common/StringToInt.h
misc/libphysfs/lzma/CPP/Common/Types.h
misc/libphysfs/lzma/CPP/Common/UTFConvert.cpp
misc/libphysfs/lzma/CPP/Common/UTFConvert.h
misc/libphysfs/lzma/CPP/Common/Wildcard.cpp
misc/libphysfs/lzma/CPP/Common/Wildcard.h
misc/libphysfs/lzma/CPP/Windows/DLL.cpp
misc/libphysfs/lzma/CPP/Windows/DLL.h
misc/libphysfs/lzma/CPP/Windows/Defs.h
misc/libphysfs/lzma/CPP/Windows/Error.cpp
misc/libphysfs/lzma/CPP/Windows/Error.h
misc/libphysfs/lzma/CPP/Windows/FileDir.cpp
misc/libphysfs/lzma/CPP/Windows/FileDir.h
misc/libphysfs/lzma/CPP/Windows/FileFind.cpp
misc/libphysfs/lzma/CPP/Windows/FileFind.h
misc/libphysfs/lzma/CPP/Windows/FileIO.cpp
misc/libphysfs/lzma/CPP/Windows/FileIO.h
misc/libphysfs/lzma/CPP/Windows/FileMapping.cpp
misc/libphysfs/lzma/CPP/Windows/FileMapping.h
misc/libphysfs/lzma/CPP/Windows/FileName.cpp
misc/libphysfs/lzma/CPP/Windows/FileName.h
misc/libphysfs/lzma/CPP/Windows/Handle.h
misc/libphysfs/lzma/CPP/Windows/MemoryLock.cpp
misc/libphysfs/lzma/CPP/Windows/MemoryLock.h
misc/libphysfs/lzma/CPP/Windows/PropVariant.cpp
misc/libphysfs/lzma/CPP/Windows/PropVariant.h
misc/libphysfs/lzma/CPP/Windows/PropVariantConversions.cpp
misc/libphysfs/lzma/CPP/Windows/PropVariantConversions.h
misc/libphysfs/lzma/CPP/Windows/StdAfx.h
misc/libphysfs/lzma/CPP/Windows/Synchronization.cpp
misc/libphysfs/lzma/CPP/Windows/Synchronization.h
misc/libphysfs/lzma/CPP/Windows/System.cpp
misc/libphysfs/lzma/CPP/Windows/System.h
misc/libphysfs/lzma/CPP/Windows/Thread.h
misc/libphysfs/lzma/CPP/Windows/Time.h
misc/libphysfs/lzma/CS/7zip/Common/CRC.cs
misc/libphysfs/lzma/CS/7zip/Common/CommandLineParser.cs
misc/libphysfs/lzma/CS/7zip/Common/InBuffer.cs
misc/libphysfs/lzma/CS/7zip/Common/OutBuffer.cs
misc/libphysfs/lzma/CS/7zip/Compress/LZ/IMatchFinder.cs
misc/libphysfs/lzma/CS/7zip/Compress/LZ/LzBinTree.cs
misc/libphysfs/lzma/CS/7zip/Compress/LZ/LzInWindow.cs
misc/libphysfs/lzma/CS/7zip/Compress/LZ/LzOutWindow.cs
misc/libphysfs/lzma/CS/7zip/Compress/LZMA/LzmaBase.cs
misc/libphysfs/lzma/CS/7zip/Compress/LZMA/LzmaDecoder.cs
misc/libphysfs/lzma/CS/7zip/Compress/LZMA/LzmaEncoder.cs
misc/libphysfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs
misc/libphysfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj
misc/libphysfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln
misc/libphysfs/lzma/CS/7zip/Compress/LzmaAlone/LzmaBench.cs
misc/libphysfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs
misc/libphysfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs
misc/libphysfs/lzma/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs
misc/libphysfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoder.cs
misc/libphysfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs
misc/libphysfs/lzma/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs
misc/libphysfs/lzma/CS/7zip/ICoder.cs
misc/libphysfs/lzma/Java/SevenZip/CRC.java
misc/libphysfs/lzma/Java/SevenZip/Compression/LZ/BinTree.java
misc/libphysfs/lzma/Java/SevenZip/Compression/LZ/InWindow.java
misc/libphysfs/lzma/Java/SevenZip/Compression/LZ/OutWindow.java
misc/libphysfs/lzma/Java/SevenZip/Compression/LZMA/Base.java
misc/libphysfs/lzma/Java/SevenZip/Compression/LZMA/Decoder.java
misc/libphysfs/lzma/Java/SevenZip/Compression/LZMA/Encoder.java
misc/libphysfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java
misc/libphysfs/lzma/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java
misc/libphysfs/lzma/Java/SevenZip/Compression/RangeCoder/Decoder.java
misc/libphysfs/lzma/Java/SevenZip/Compression/RangeCoder/Encoder.java
misc/libphysfs/lzma/Java/SevenZip/ICodeProgress.java
misc/libphysfs/lzma/Java/SevenZip/LzmaAlone.java
misc/libphysfs/lzma/Java/SevenZip/LzmaBench.java
misc/libphysfs/lzma/LGPL.txt
misc/libphysfs/lzma/Methods.txt
misc/libphysfs/lzma/history.txt
misc/libphysfs/lzma/lzma.txt
misc/libphysfs/physfs.c
misc/libphysfs/physfs.h
misc/libphysfs/physfs_byteorder.c
misc/libphysfs/physfs_casefolding.h
misc/libphysfs/physfs_internal.h
misc/libphysfs/physfs_miniz.h
misc/libphysfs/physfs_platforms.h
misc/libphysfs/physfs_unicode.c
misc/libphysfs/platform_beos.cpp
misc/libphysfs/platform_macosx.c
misc/libphysfs/platform_posix.c
misc/libphysfs/platform_unix.c
misc/libphysfs/platform_windows.c
misc/libphysfs/platform_winrt.cpp
misc/winutils/include/SDL.h
misc/winutils/include/SDL_active.h
misc/winutils/include/SDL_audio.h
misc/winutils/include/SDL_byteorder.h
misc/winutils/include/SDL_cdrom.h
misc/winutils/include/SDL_config.h
misc/winutils/include/SDL_config_win32.h
misc/winutils/include/SDL_copying.h
misc/winutils/include/SDL_cpuinfo.h
misc/winutils/include/SDL_endian.h
misc/winutils/include/SDL_error.h
misc/winutils/include/SDL_events.h
misc/winutils/include/SDL_getenv.h
misc/winutils/include/SDL_joystick.h
misc/winutils/include/SDL_keyboard.h
misc/winutils/include/SDL_keysym.h
misc/winutils/include/SDL_loadso.h
misc/winutils/include/SDL_main.h
misc/winutils/include/SDL_mixer.h
misc/winutils/include/SDL_mouse.h
misc/winutils/include/SDL_mutex.h
misc/winutils/include/SDL_name.h
misc/winutils/include/SDL_net.h
misc/winutils/include/SDL_opengl.h
misc/winutils/include/SDL_platform.h
misc/winutils/include/SDL_quit.h
misc/winutils/include/SDL_rwops.h
misc/winutils/include/SDL_stdinc.h
misc/winutils/include/SDL_syswm.h
misc/winutils/include/SDL_thread.h
misc/winutils/include/SDL_timer.h
misc/winutils/include/SDL_types.h
misc/winutils/include/SDL_version.h
misc/winutils/include/SDL_video.h
misc/winutils/include/begin_code.h
misc/winutils/include/close_code.h
misc/winutils/include/png.h
misc/winutils/include/pngconf.h
misc/winutils/include/zconf.h
misc/winutils/include/zlib.h
misc/winutils/lib/libSDL.dll.a
misc/winutils/lib/libSDL_image.dll.a
misc/winutils/lib/libSDL_mixer.dll.a
misc/winutils/lib/libSDL_net.dll.a
misc/winutils/lib/libSDLmain.a
project_files/HedgewarsMobile/Default-568h@2x.png
project_files/HedgewarsMobile/Locale/Bulgarian.lproj/About.strings
project_files/HedgewarsMobile/Locale/Bulgarian.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Bulgarian.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/Danish.lproj/About.strings
project_files/HedgewarsMobile/Locale/Danish.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Danish.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/English.lproj/About.strings
project_files/HedgewarsMobile/Locale/English.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/English.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/French.lproj/About.strings
project_files/HedgewarsMobile/Locale/French.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/French.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/German.lproj/About.strings
project_files/HedgewarsMobile/Locale/German.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/German.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/Italian.lproj/About.strings
project_files/HedgewarsMobile/Locale/Italian.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Italian.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/Japanese.lproj/About.strings
project_files/HedgewarsMobile/Locale/Japanese.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Japanese.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/Polish.lproj/About.strings
project_files/HedgewarsMobile/Locale/Polish.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Polish.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/Portuguese.lproj/About.strings
project_files/HedgewarsMobile/Locale/Portuguese.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Portuguese.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/Romanian.lproj/About.strings
project_files/HedgewarsMobile/Locale/Romanian.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Romanian.lproj/Scheme.strings
project_files/HedgewarsMobile/Locale/Spanish.lproj/About.strings
project_files/HedgewarsMobile/Locale/Spanish.lproj/Localizable.strings
project_files/HedgewarsMobile/Locale/Spanish.lproj/Scheme.strings
project_files/HedgewarsMobile/Resources/Icons/Default-ipad-Landscape.png
project_files/HedgewarsMobile/Resources/Icons/Default.png
project_files/HedgewarsMobile/Resources/Icons/Default@2x.png
project_files/HedgewarsMobile/Resources/Icons/Icon-60@2x.png
project_files/HedgewarsMobile/Resources/Icons/Icon-72.png
project_files/HedgewarsMobile/Resources/Icons/Icon-76.png
project_files/HedgewarsMobile/Resources/Icons/Icon-76@2x.png
project_files/HedgewarsMobile/Resources/Icons/Icon-Small-50.png
project_files/HedgewarsMobile/Resources/Icons/Icon-Small.png
project_files/HedgewarsMobile/Resources/Icons/Icon-Small@2x.png
project_files/HedgewarsMobile/Resources/Icons/Icon.png
project_files/HedgewarsMobile/Resources/Icons/Icon@2x.png
project_files/HedgewarsMobile/Resources/Icons/iTunesArtwork.png
share/hedgewars/Data/Graphics/Hats/Luigi.svg
share/hedgewars/Data/Graphics/Hats/Mario.svg
share/hedgewars/Data/Graphics/Hats/PrincessPeach.svg
share/hedgewars/Data/Graphics/Hats/Toad.svg
share/hedgewars/Data/Graphics/Hats/Wario.svg
share/hedgewars/Data/Graphics/Hats/pikachu.svg
share/hedgewars/Data/Graphics/slider.png
share/hedgewars/Data/Locale/ar.txt
share/hedgewars/Data/Locale/hedgewars_ar.ts
share/hedgewars/Data/Sounds/voices/British/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/British/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Classic/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Classic/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Default_es/Coward.ogg
share/hedgewars/Data/Sounds/voices/Default_uk/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Default_uk/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Mobster/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Mobster/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Pirate/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Pirate/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Robot/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Robot/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Russian/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Russian/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Singer/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Singer/PoisonMoan.ogg
share/hedgewars/Data/Sounds/voices/Surfer/PoisonCough.ogg
share/hedgewars/Data/Sounds/voices/Surfer/PoisonMoan.ogg
share/hedgewars/Data/Themes/Hoggywood/SkyL.png
share/hedgewars/Data/Themes/Hoggywood/SkyR.png
tests/lua/staticmines.lua
tools/build_windows.bat
tools/drawMapTest/drawMapTest.pro
tools/drawMapTest/drawmapscene.cpp
tools/drawMapTest/drawmapscene.h
tools/drawMapTest/drawmapwidget.cpp
tools/drawMapTest/drawmapwidget.h
tools/drawMapTest/main.cpp
tools/drawMapTest/mainwindow.cpp
tools/drawMapTest/mainwindow.h
tools/drawMapTest/mainwindow.ui
tools/drawMapTest/qaspectratiolayout.cpp
tools/drawMapTest/qaspectratiolayout.h
tools/templates/main.cpp
tools/templates/mainform.cpp
tools/templates/mainform.h
tools/templates/pixlabel.cpp
tools/templates/pixlabel.h
tools/templates/templates.pro
tools/w32DownloadUnzip.vbs
--- a/.gitignore	Wed May 16 18:22:28 2018 +0200
+++ b/.gitignore	Wed Jul 31 23:14:27 2019 +0200
@@ -81,3 +81,5 @@
 *.mode1v3
 *.mode2v3
 Testing/*
+gameServer2/rls
+gameServer2/target
--- a/.hgignore	Wed May 16 18:22:28 2018 +0200
+++ b/.hgignore	Wed Jul 31 23:14:27 2019 +0200
@@ -16,11 +16,11 @@
 *.ppu
 *.*~
 *.core
-hedgewars.pro.user
 config.inc
 cmake_install.cmake
 QTfrontend/hwconsts.cpp
 QTfrontend/servermessages.h
+QTfrontend/creditsmessages.h
 CPackConfig.cmake
 CPackSourceConfig.cmake
 tools/cmake_uninstall.cmake
@@ -44,6 +44,7 @@
 *.rej
 project_files/hwc/*.c
 project_files/hwc/*.h
+project_files/hwc/*.out
 project_files/Android-build/SDL-android-project/jni/**
 project_files/Android-build/SDL-android-project/obj
 project_files/Android-build/SDL-android-project/libs/armeabi*
@@ -82,8 +83,12 @@
 xcuserdata
 *.mode1v3
 *.mode2v3
-gameServer2/target
-gameServer2/Cargo.lock
-gameServer2/gameServer2.iml
 .idea
 Testing/
+rust/*/target
+*.lock
+*.user
+*.iml
+build-qmlfrontend*
+.cabal-sandbox
+cabal.sandbox.config
--- a/.hgtags	Wed May 16 18:22:28 2018 +0200
+++ b/.hgtags	Wed Jul 31 23:14:27 2019 +0200
@@ -87,3 +87,5 @@
 0000000000000000000000000000000000000000 0.9.24.1-release
 0000000000000000000000000000000000000000 0.9.24.1-release
 afc089c39556bdd895892fa36ed47aaec83c3825 0.9.24.1-release
+195208deff1dd3e22d303d4a92c2ba14be3b6623 Hedgewars-iOS-2.1
+5e28098fb59379357a145b73380a1cd3839f643f 0.9.25-release
--- a/.travis.yml	Wed May 16 18:22:28 2018 +0200
+++ b/.travis.yml	Wed Jul 31 23:14:27 2019 +0200
@@ -23,7 +23,7 @@
   - BUILD_ARGS="-DCMAKE_BUILD_TYPE=Release"
   - BUILD_ARGS="-DCMAKE_BUILD_TYPE=Debug"
   - BUILD_ARGS="-DNOSERVER=1 -DGL2=1 -DNOPNG=1"
-  - BUILD_ARGS="-DNOSERVER=1 -DLUA_SYSTEM=0 -DPHYSFS_SYSTEM=0"
+  - BUILD_ARGS="-DNOSERVER=1 -DLUA_SYSTEM=0"
 
 matrix:
   include:
@@ -54,7 +54,7 @@
   if [ "$TRAVIS_OS_NAME" == "linux" ]; then
     sudo apt-get update -qq
   elif [ "$TRAVIS_OS_NAME" == "osx" ]; then
-    brew update --all
+    brew update
   elif [ "$TRAVIS_OS_NAME" == "ios" ]; then
     hg clone http://hg.libsdl.org/SDL $SDL_LIB_PATH/SDL/
     hg clone http://hg.libsdl.org/SDL_image $SDL_LIB_PATH/SDL_image/
@@ -65,11 +65,22 @@
 
 install: |
   if [ "$TRAVIS_OS_NAME" == "linux" ]; then
-    sudo apt-get install -y debhelper cmake dpkg-dev qtbase5-dev qtbase5-private-dev qttools5-dev-tools qttools5-dev libphysfs-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-net-dev bzip2 ghc libghc-mtl-dev libghc-vector-dev libghc-zlib-dev libghc-random-dev libghc-network-dev libghc-sandi-dev libghc-hslogger-dev libghc-utf8-string-dev libghc-sha-dev libghc-entropy-dev libghc-regex-tdfa-dev liblua5.1-0-dev fpc fp-compiler fp-units-misc libpng-dev fp-units-gfx libavcodec-dev libavformat-dev libglew1.6-dev
+    sudo apt-get install -y debhelper cmake dpkg-dev qtbase5-dev qtbase5-private-dev qttools5-dev-tools qttools5-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-net-dev bzip2 ghc libghc-mtl-dev libghc-vector-dev libghc-zlib-dev libghc-random-dev libghc-network-dev libghc-sandi-dev libghc-hslogger-dev libghc-utf8-string-dev libghc-sha-dev libghc-entropy-dev libghc-regex-tdfa-dev libghc-aeson-dev libghc-yaml-dev libghc-text-dev liblua5.1-0-dev fpc fp-compiler fp-units-misc libpng-dev fp-units-gfx libavcodec-dev libavformat-dev libglew1.6-dev
+
+    # for xenial last availible version of libphysfs is 2.0.x, but we need >= 3.0
+    # so... building from sources!
+    wget https://icculus.org/physfs/downloads/physfs-3.0.1.tar.bz2
+    tar -xjf physfs-3.0.1.tar.bz2
+    mkdir physfs-3.0.1-build
+    pushd physfs-3.0.1-build
+    cmake ../physfs-3.0.1
+    make
+    sudo make install
+    popd
   elif [ "$TRAVIS_OS_NAME" == "osx" ]; then
     brew install qt5
     brew install fpc glew physfs lua51 sdl2 sdl2_image sdl2_net sdl2_ttf ffmpeg ghc cabal-install
-    brew install sdl2_mixer --with-libvorbis
+    brew install sdl2_mixer
     # use cabal install haskell deps, pas2c ones are covered by server
     if [[ "$BUILD_ARGS" != *"NOSERVER"* ]]; then
       cabal update
--- a/CMakeLists.txt	Wed May 16 18:22:28 2018 +0200
+++ b/CMakeLists.txt	Wed Jul 31 23:14:27 2019 +0200
@@ -27,10 +27,8 @@
 option(BUILD_SHARED_LIBS "Build libraries as shared modules (on)" ON)
 
 if(WIN32 OR APPLE)
-    option(PHYSFS_SYSTEM "Use system physfs (off)" OFF)
     option(LUA_SYSTEM "Use system lua (off)" OFF)
 else()
-    option(PHYSFS_SYSTEM "Use system physfs (on)" ON)
     option(LUA_SYSTEM "Use system lua (on)" ON)
 endif()
 
@@ -53,6 +51,14 @@
 option(NOVERSIONINFOUPDATE "Disable update of version_info.txt. To be used if source is in a git/repo that is NOT the hedgewars repo" OFF)
 
 
+if(BUILD_ENGINE_C AND NOT NOVIDEOREC)
+    if((CMAKE_BUILD_TYPE MATCHES "RELEASE") OR (CMAKE_BUILD_TYPE MATCHES "RELWITHDEBUGINFO"))
+        message("NOTE: Video recorder support disabled. It's incompatible with BUILD_ENGINE_C")
+        set(BUILD_ENGINE_C ON CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
+    else()
+        message("WARNING: Video recorder support is currently incompatible with BUILD_ENGINE_C, the video recorder won't work (but demos are fine)! See <https://issues.hedgewars.org/show_bug.cgi?id=722>.")
+    endif()
+endif()
 if(BUILD_ENGINE_JS)
     if(NOT CMAKE_TOOLCHAIN_FILE)
         message(FATAL_ERROR "Missing emscripten toolchain file\nClean your cache and rerun cmake with -DCMAKE_TOOLCHAIN_FILE=${CMAKE_SOURCE_DIR}/cmake_modules/Platform/Emscripten.cmake")
@@ -61,7 +67,6 @@
     set(BUILD_ENGINE_C ON CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
     set(BUILD_ENGINE_LIBRARY ON CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
     set(NOAUTOUPDATE ON CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
-    set(PHYSFS_SYSTEM OFF CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
     set(LUA_SYSTEM OFF CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
     set(NOVIDEOREC ON CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
     set(NOSERVER ON CACHE STRING "Required for BUILD_ENGINE_JS" FORCE)
@@ -72,17 +77,25 @@
     set(target_library_install_dir "lib" CACHE PATH "install dest for libs")
 endif()
 
+if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4" AND UNIX AND NOT APPLE)
+    set(BUILD_ENGINE_C ON CACHE STRING "PAS2C force-enabled due to a freepascal 32 bit alignment bug" FORCE)
+endif()
+
 #system paths for finding required fonts (see share/hedgewars/Data/fonts)
 #subdirectories will NOT be searched.
 #all fonts that can't be found will be bundled with hedgewars
 set(FONTS_DIRS "" CACHE STRING "Additional paths to folders where required fonts can be found ( ; is separator)")
 
 #versioning
-set(CPACK_PACKAGE_VERSION_MAJOR 0)
-set(CPACK_PACKAGE_VERSION_MINOR 9)
-set(CPACK_PACKAGE_VERSION_PATCH "24.1")
-set(HEDGEWARS_PROTO_VER 55)
-set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
+set(CPACK_PACKAGE_VERSION_MAJOR 1)
+set(CPACK_PACKAGE_VERSION_MINOR 0)
+set(CPACK_PACKAGE_VERSION_PATCH 0)
+set(HEDGEWARS_PROTO_VER 58)
+if((CMAKE_BUILD_TYPE MATCHES "RELEASE") OR (CMAKE_BUILD_TYPE MATCHES "RELWITHDEBUGINFO"))
+    set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
+else()
+    set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-dev")
+endif()
 include(${CMAKE_MODULE_PATH}/revinfo.cmake)
 
 message(STATUS "Building ${HEDGEWARS_VERSION}-r${HEDGEWARS_REVISION} (${HEDGEWARS_HASH})")
@@ -116,12 +129,21 @@
 endif()
 
 #set default compiler flags
-add_flag_append(CMAKE_C_FLAGS "-Wall -pipe")
-add_flag_append(CMAKE_C_FLAGS_RELEASE "-O2")
-add_flag_append(CMAKE_C_FLAGS_DEBUG "-Wextra -O0")
-add_flag_append(CMAKE_CXX_FLAGS "-Wall -pipe")
-add_flag_append(CMAKE_CXX_FLAGS_RELEASE "-O2")
-add_flag_append(CMAKE_CXX_FLAGS_DEBUG "-Wextra -O0")
+if(WIN32 AND VCPKG_TOOLCHAIN)
+    add_flag_append(CMAKE_C_FLAGS "/DWIN32_VCPKG /Wall")
+    add_flag_append(CMAKE_C_FLAGS_RELEASE "/Ox")
+    add_flag_append(CMAKE_C_FLAGS_DEBUG "/Od")
+    add_flag_append(CMAKE_CXX_FLAGS "/DWIN32_VCPKG /Wall")
+    add_flag_append(CMAKE_CXX_FLAGS_RELEASE "/Ox")
+    add_flag_append(CMAKE_CXX_FLAGS_DEBUG "/Od")    
+else()
+    add_flag_append(CMAKE_C_FLAGS "-Wall -pipe")
+    add_flag_append(CMAKE_C_FLAGS_RELEASE "-O2")
+    add_flag_append(CMAKE_C_FLAGS_DEBUG "-Wextra -O0")
+    add_flag_append(CMAKE_CXX_FLAGS "-Wall -pipe")
+    add_flag_append(CMAKE_CXX_FLAGS_RELEASE "-O2")
+    add_flag_append(CMAKE_CXX_FLAGS_DEBUG "-Wextra -O0")    
+endif()       
 
 #CMake adds a lot of additional configuration flags, so let's clear them up
 if(MINIMAL_FLAGS)
@@ -199,40 +221,32 @@
 
 
 #physfs discovery
-if(PHYSFS_SYSTEM)
-    if(NOT PHYSFS_LIBRARY OR NOT PHYSFS_INCLUDE_DIR)
-        find_package(PhysFS)
-    endif()
-
-    find_file(physfs_h physfs.h ${PHYSFS_INCLUDE_DIR})
-    if(physfs_h)
-        file(STRINGS ${physfs_h} physfs_majorversion REGEX "PHYSFS_VER_MAJOR[\t' ']+[0-9]+")
-        file(STRINGS ${physfs_h} physfs_minorversion REGEX "PHYSFS_VER_MINOR[\t' ']+[0-9]+")
-        file(STRINGS ${physfs_h} physfs_patchversion REGEX "PHYSFS_VER_PATCH[\t' ']+[0-9]+")
-        string(REGEX MATCH "([0-9]+)" physfs_majorversion "${physfs_majorversion}")
-        string(REGEX MATCH "([0-9]+)" physfs_minorversion "${physfs_minorversion}")
-        string(REGEX MATCH "([0-9]+)" physfs_patchversion "${physfs_patchversion}")
-        set(physfs_detected_ver "${physfs_majorversion}.${physfs_minorversion}.${physfs_patchversion}")
+if(NOT PHYSFS_LIBRARY OR NOT PHYSFS_INCLUDE_DIR)
+    find_package(PhysFS)
+endif()
 
-        if(${physfs_detected_ver} VERSION_LESS 2.0.0)
-            message(FATAL_ERROR "PhysFS version is too old (detected ${physfs_detected_ver}, required 2.0.0)\n"
-                                "Perform an update or rerun cmake with -DPHYSFS_SYSTEM=off to build the internal version")
-        endif()
-    endif()
+find_file(physfs_h physfs.h ${PHYSFS_INCLUDE_DIR})
+if(physfs_h)
+    file(STRINGS ${physfs_h} physfs_majorversion REGEX "PHYSFS_VER_MAJOR[\t' ']+[0-9]+")
+    file(STRINGS ${physfs_h} physfs_minorversion REGEX "PHYSFS_VER_MINOR[\t' ']+[0-9]+")
+    file(STRINGS ${physfs_h} physfs_patchversion REGEX "PHYSFS_VER_PATCH[\t' ']+[0-9]+")
+    string(REGEX MATCH "([0-9]+)" physfs_majorversion "${physfs_majorversion}")
+    string(REGEX MATCH "([0-9]+)" physfs_minorversion "${physfs_minorversion}")
+    string(REGEX MATCH "([0-9]+)" physfs_patchversion "${physfs_patchversion}")
+    set(physfs_detected_ver "${physfs_majorversion}.${physfs_minorversion}.${physfs_patchversion}")
 
-    if(PHYSFS_LIBRARY AND PHYSFS_INCLUDE_DIR)
-        #use an IMPORTED tharget so that we can just use 'physfs' to link
-        add_library(physfs UNKNOWN IMPORTED)
-        set_target_properties(physfs PROPERTIES IMPORTED_LOCATION ${PHYSFS_LIBRARY})
-    else()
-        message(FATAL_ERROR "Missing PhysFS! Rerun cmake with -DPHYSFS_SYSTEM=off to build the internal version")
+    if(${physfs_detected_ver} VERSION_LESS 3.0.0)
+        message(FATAL_ERROR "PhysFS version is too old (detected ${physfs_detected_ver}, required 3.0.0)\n"
+                            "Perform an update of PhysFS to fix this.")
     endif()
+endif()
+
+if(PHYSFS_LIBRARY AND PHYSFS_INCLUDE_DIR)
+    #use an IMPORTED tharget so that we can just use 'physfs' to link
+    add_library(physfs UNKNOWN IMPORTED)
+    set_target_properties(physfs PROPERTIES IMPORTED_LOCATION ${PHYSFS_LIBRARY})
 else()
-    if(NOT PHYSFS_LIBRARY OR NOT PHYSFS_INCLUDE_DIR)
-        message(STATUS "PhysFS will be provided by the bundled sources")
-    endif()
-    set(physfs_output_name "hwphysfs")
-    add_subdirectory(misc/libphysfs)
+    message(FATAL_ERROR "Missing PhysFS! Install PhysFS to fix this.")
 endif()
 
 find_package_or_disable_msg(LIBAV NOVIDEOREC "Video recording will not be built")
--- a/CREDITS	Wed May 16 18:22:28 2018 +0200
+++ b/CREDITS	Wed Jul 31 23:14:27 2019 +0200
@@ -1,6 +1,7 @@
 =============================
 === EXTENDED CREDITS LIST ===
 =============================
+For the main credits, click on the Hedgewars logo in the main menu.
 
 IF NOT OTHERWISE SPECIFIED, ALL OTHER CONTENT IS PROPERTY OF Andrey Korotaev <unC0Rr@gmail.com>.
 IF NO LICENSE IS SPECIFIED, THE LICENSE IS THE SAME AS MENTIONED IN README.md.
@@ -12,26 +13,6 @@
 ==========
 - see Fonts_LICENSE.txt
 
-=================
-= FRONTEND IMAGES
-=================
-(File name suffixes are omitted)
-
-- Tango Project and Wuzzy -> audio, home (CC0)
-- abustany and Wuzzy -> Videos (CC0)
-- Juliane Krug and Wuzzy -> Palette (CC0)
-- raseone and Wuzzy -> folder (CC0)
-
-==========
-= FORTS
-==========
-- Carlos Vives -> Tank (2010)
-- Dragonfly -> EvilChicken (2010)
-- Randy Broda -> SteelTower (2013)
-- Jon Dum and Wuzzy -> Snail (2017)
-- Maciej Mroziński (a.k.a. alzen) and Wuzzy -> Lonely_Island (2017)
-- Guillaume Englert and Wuzzy -> Olympic (2017)
-
 ==========
 = HATS
 ==========
@@ -51,57 +32,32 @@
 - Terrington_Snyde -> pirate_eyepatch (2013), jester (2013)
 - Wohlstand -> policegirl [based on policecap and sm_daisy] (2014)
 - TheMadCharles -> barrelhider (CC BY 3.0) (2015)
+- Trey Perry <perry.j@gmail.com> -> Other hats
 
 ==========
-= GRAVESTONES
+= GRAVES
 ==========
 - Randy Broda -> dragonball (2012)
 - CheezeMonkey -> pi (2011)
 - rosenholz -> Whisky (2013)
 
-==========
-= MAPS
-==========
-- John Dum <fizzy@gmail.com> -> Bath (2008), Hedgelove (2008), Hedgewars (2008), Hydrant (2008), mushrooms (2008), Plane (2008)
-- Joshua Frese <joshfrese@gmail.com> -> Bamboo (2008), EarthRise (2008), Freeway (2008), BambooPlinko (2008)
-- Stanko Tadić <stanko@mfhinc.net> -> Castle (2008), PirateFlag (2008)
-- dctPL -> Sticks (2010)
-- wolfmarc & Dragonfly -> TrophyRace (2010), ShoppaKing (2010)
+=================
+= FRONTEND IMAGES
+=================
+(File name suffixes are omitted)
 
-==========
-= MUSIC
-==========
-- HSR ( http://elhombresinremedio.com ) -> (new) City theme, Rock theme and many other tracks
-- John Dum <fizzy@gmail.com> -> Nature theme
-- Jonatan Nilsson <jonatanfan@gmail.com> -> Pirate theme, EarthRise (former City) theme, Oriental theme, Snow theme)
-- yd - http://opengameart.org/users/yd -> "oriented", used as Olympics SD theme
-- Kevin MacLeod - http://incompetech.com/ -> "hitman", used as basis for preliminary default SD theme
-- Valentin Kraevskiy (alias alfadur) -> Jungle theme, Fruit theme
+- Tango Project and Wuzzy -> audio, home (CC0)
+- abustany and Wuzzy -> Videos (CC0)
+- Juliane Krug and Wuzzy -> Palette (CC0)
+- raseone and Wuzzy -> folder (CC0)
+
+==================
+= OTHER GRAPHICS =
+==================
+- Custom ammo images for Continental supplies: KarBoy2314PL
 
 ==========
-= THEMES
-==========
-- John Dum <fizzy@gmail.com> -> Nature (2008), Snow (2008), City
-- Joshua Frese <joshfrese@gmail.com> -> Bamboo (2008), EarthRise (2008), Freeway (2008), BambooPlinko (2008)
-- Stanko Tadić <stanko@mfhinc.net> -> Hell (2008)
-- Julien Koesten <julienkoesten@aol.com> -> Sheep (2008)
-- KoRn666 - Jungle (2010)
-- Randy Broda -> Fruit (2013), Cake (2014)
-
-==========
-= VOICES
-==========
-- Stephen Alexander <ArmagonNo1@gmail.com>
-- mtg90pl <mtg90pl@gmail.com>: Default_pl, Russian_pl
-
-==========
-= MISSIONS
-==========
-- Arkhnen -> Teamwork 2 (2012)
-- Wuzzy -> Big Armory (2016)
-
-==========
-= SOUNDS
+= SOUND EFFECTS
 ==========
 - Mine impact sound from http://www.freesound.org/people/adcbicycle/sounds/13947/
 - Hammer sound from http://www.freesound.org/people/Syna-Max/sounds/43586/
@@ -124,17 +80,20 @@
      https://www.freesound.org/people/Jagadamba/sounds/257057/
 - Blowtorch sound originally by rombard (CC-0), remixed from
      https://www.freesound.org/people/rombart/sounds/197800/
+- Flamethrower sound originally by AslakHostaker (CC-0), adapted from
+     https://freesound.org/people/AslakHostaker/sounds/395039/
+- Landspray sound originally by Benboncan (CC BY 3.0), remixed from
+     https://freesound.org/people/Benboncan/sounds/82390/
 - Portable Portal Device color switching sound by Wuzzy (CC-0)
 - Portable Portal Device shot sound originally by bubaproducer (CC-BY 3.0), remixed from
      https://www.freesound.org/people/bubaproducer/sounds/151026/ 
 - Portal opening sound by Wuzzy (CC-0)
 - Invulnerable sound: remix based on a sound by pepingrillin (CC-0)
      https://www.freesound.org/people/pepingrillin/sounds/252079/
-
-==================
-= OTHER GRAPHICS =
-==================
-- Custom ammo images for Continental supplies: KarBoy2314PL
+- Droplet1.ogg, Droplet2.ogg, Droplet3.ogg: based off a recording by saugox (CC-BY 3.0)
+     https://freesound.org/people/saugox/sounds/37548/
+- Shoryuken hit: Based off sound by CGEffex (CC-BY 3.0), tweaked by alfadur
+     http://freesound.org/people/CGEffex/sounds/98341/
 
 ======================
 = LICENSE REFERENCES =
--- a/ChangeLog.txt	Wed May 16 18:22:28 2018 +0200
+++ b/ChangeLog.txt	Wed Jul 31 23:14:27 2019 +0200
@@ -1,12 +1,433 @@
 + features
 * bugfixes
-====================== 0.9.25 ======================
+======================= ??? ========================
 User Interface:
  + In-Game chat size can now be adjusted. Hold Ctrl and press -, + or = while in chat input. Hold shift for finer control.
  + The intial in-game chat size can be configured in the Frontend's "advanced" settings tab.
 
+============== 1.0.0-dev (unreleased) ==============
+Highlights:
+ + Campaigns now respect your team identity instead of overwriting it
+ + Single missions now support team selection and track your progress
+ + Challenges track the team's highscores
+ + Hand-drawn maps can now be scaled with slider
+ + Quick games are more random
+ + Homing bee can be used as secondary ammo
+ + Can change hedgehog order in The Specialists
+ + Turn transition is less hectic
+ + Various small HUD improvements
+ * Fix wrong key names being displayed in key selection
+
+Gameplay:
+ + Quick games are more random: More map types, random team size and difficulty
+ + Hand-drawn maps can now be scaled with slider
+ + Slightly longer delays between turns to make it easier to follow the game
+ + Track high scores in singleplayer challenges
+ + Show check mark for completed scenarios, challenges and trainings
+ + Training/challenge/scenario menu now supports team selection
+ + Most target practices now highlight position of next target (must be unlocked first)
+ + Homing bee can now be used as secondary ammo
+ + If bee target was placed in the dark area in a wrap world edge map, bee will first fly across border
+ + Teach computer players how to use extra time
+ * Fix hedgehogs being pushed around (and other collision bugs) when they overlap
+ * Fix homing bee flying weird if passing wrap world edge or target was placed beyond it
+ * Fix air mine not colliding with crates initially
+ * Fix buggy behaviour of time box if hog took damage or died before it arrived
+ * Fix poison damage not working in first round
+ * Use player-chosen team identity in campaigns and singleplayer missions
+ * Fix player-chosen teams ignoring custom team controls in campaigns
+ * Fix broken behaviour of airborne attacks when placed near bounce world edge
+ * Fix crate sometimes collected twice when switching to hedgehog that touches it
+ * Deny placement of piano beyond bounce world edge
+ * Fix laser sight not working properly when it starts out of map bounds
+ * Fix parachute making hog stuck or fast when bumping into wall while looking other way
+ * Add missing winner animation in single missions
+ * Fix hog floating when switching to moving hog
+ * Fix jump key not being ignored after placing girder or target
+ * Explode hog instantly when taking damage while dying
+ * Fix buggy hog when hog took damage during "idle" phase in kamikaze attack
+
+Styles and schemes:
+ + The Specialists: Unlock game scheme
+ + The Specialists: Add script parameter support to set custom specialists order
+ + Control, CTF_Blizzard: Display scores in stats screen
+ + CTF_Blizzard: Various minor graphical and text improvements
+ + Frenzy: Change ammo slots
+ + Continental supplies: Show continent in team bars
+ * Balanced Random Weapon: Fix Lua errors after using Time Box
+ * Racer: Fix racer ghost not getting reset after a skip
+ * Space Invasion: No longer allow to set start shield above shield limit
+ * Battalion, WxW: Crates drop between turns, when appropriate
+ * Battalion: Sudden Death effects are now like in the base game
+ * King Mode: Fix team sometimes not being killed properly if king drowned
+ * King Mode: Kill resurrected minions if king is not alive
+ * King Mode: Fix whole clan being killed if a king died
+ * King Mode: Fix king placement phase not working correctly with multiple teams in a clan
+ * HedgeEditor: Fix major FPS drop when there are a lot of objects
+ * Control: Fix score failure after using extra time
+ * Frenzy: Fix incorrect ammo slot numbers in ammo menu
+ * Continental supplies: Computer teams now select random continent
+ * WxW, Racer: Computer teams no longer block setup phase
+ * Mutant: Delete excess teams when a clan has more than one team
+
+A Classic Fairytale:
+ + Backstab: Disable utilities before traitor has been dealt with
+ * Backstab: Prevent attacking the cannibals before making the choice
+ * Backstab: Fix/tweak behaviour in 3rd enemy wave
+ * First blood: Fix Lua error when hitting Attack after failing the rope challenge
+ * First blood: Fix a cut scene being played twice in row
+ * The Shadow Falls: Fix Lua error when hog dies during choice phase
+ * The Shadow Falls: Fix mission getting stuck when hog dies after accepting offer, but before returning
+ * The Shadow Falls: Fix many other Lua errors when hogs die in certain situations
+ * General: Clear hazards around cyborg when it appears in cut scenes
+ * General: Disable Sudden Death for all missions
+ * Various minor tweaks and bugfixes
+
+A Space Adventure:
+ + Show your current records at mission start when re-playing one of the challenges
+ + Spacetrip: Move flowers of desert planet above cactus
+ + Searching in the dust: Enable skip in entire mission
+ + Getting to the device: Different ending when hero chose to battle in "Bad timing" mission
+ * Searching in the dust: Fix mission ending when all smugglers are dead
+ * Searching in the dust: Fix a lot of broken/stupid smuggler behaviours
+ * Chasing the blue hog: Fix player not losing the race when timing out while still having the rope
+ * Chasing the blue hog: Fix player winning if Crazy Runner died
+ * Bad timing: Win mission in "flee" variant if all enemy hogs are dead
+ * Getting to the device: Fix clan colors
+ * Fix errors when hero and enemies die in same turn
+ * Various minor tweaks and bugfixes
+
+Controls:
+ + Add control to unselect current weapon (no key chosen by default)
+ + Add support for 4th and 5th mouse buttons
+ + Allow to leave a control unused
+ + Reset zoom resets zoom to zoom level set in options
+ + Add control to display mines time and health crate health (default: O)
+ + Precise + Reset zoom resets zoom to 100% (instead of zoom in options)
+ + Precise + zoom in/out changes zoom in smaller steps
+ + Precise + volume up/down changes volume in smaller steps
+ + Precise + cursor move keys move camera slower
+ + New chat command: “/help room” (shows room chat commands within the game)
+ + Default demo fast-forward key changed from “S” to “F”
+ * Fix broken default keyboard controls for team chat and camera movement
+
+Graphics:
+ + Animate drill rockets
+ + New idle shoryuken animation
+ + Scatter molotov cocktail pieces
+ + Improve air plane effects when used with wrap or ocean world edge
+ * Fix speech bubbles overlapping in the wrong order
+ * Fix wrong ice beam angle if it goes diagonally up out of map through world wrap
+ * Fix double water splash when flying saucer drowns
+ * Fix odd floating pixels when wielding and rotating cleaver
+ * Fix parachute and birdy sometimes being drawn behind hedgehogs and objects
+
+Game HUD:
+ + Display current hog health (and related status icons) at top right corner
+ + Display laser sight icon above wind bar when laser sight utility is active
+ + Display selected weapon above hedgehog for some weapons/tools
+ + Change cursor of piano strike
+ + Colorize switching arrows, pointing arrow and target cross in clan color
+ + Skip ammo menu animation when playing with turn time of 10s or less
+ + Don't show crate spawn message for initial crates in missions
+ + Don't show hedgehog health if “invulnerable” game modifier is active
+ + Display player name of own teams in online games
+ + Show contour of flying saucer and air mines when in highly opaque water
+ + Remove visual clutter in cut scenes
+ + Add setting to set default/initial zoom
+ * Black clan color can now be used without visual problems
+ * Fix last 2 characters in demo chat being missing
+ * Hide most HUD elements in cinematic mode
+ * Don't show "F1", "F2", etc. in ammo menu if these aren't the actual slot keys
+ * Fix wind bar animation not looping properly
+ * Fix airplane line being drawn above many HUD elements
+ * Suppress “<team> is gone.” message at end of game
+ * Fix game engine ignoring appropriate number formatting of user language
+ * Fix buggy behaviour when entering speech bubble command in hog placement phase
+
+Translations:
+ + Complete: German, Polish
+ + Major updates: Chinese, Scottish Gaelic
+ + Credits page is now translatable
+ * Remove Arabic translation from release
+
+Frontend:
+ + Add button in main menu at top left corner to open credits page
+ + Restructure credits page
+ + More intelligent automatic mission selection in campaign screen
+ + New data directory for video thumbnails: Data/VideoThumbnails
+ + Display a warning when the same key is used multiple times
+ + Stats screen now hides empty sections
+ + Visual notification when someone joins the room online
+ + Display recommended max. hedgehog count for Perlin maps
+ + Various minor style tweaks
+ * Fix broken handling of /watch chat command on official server
+ * Fix renaming a video leading to loss of thumbnail after restart
+ * Fix controls list failing to display correct key names with regards to keyboard layout
+ * Fix force-locked schemes getting unlocked when changing map types
+ * Fix possible to select background-only or hidden themes indirectly by changing map type
+ * Disallow slash, backslash and colon characters in team and scheme names
+
+Sounds and voicepacks:
+ + sndYoohoo has been split to sndYoohoo and sndKiss
+ + Voice files sndPoisonCough and sndPoisonMoan are now optional (fall back to Default voicepack)
+ + Add taunt: sndFlyAway / Flyaway.ogg: When hedgehog flies off the map
+ + Add underwater sound for airplane
+ + Tweak some taunts: sndFirstBlood, sndLeaveMeAlone, sndCutItOut
+ * Fix English voicepack selection of team being overwritten when playing in non-English locale
+
+Theme customization
+ + Default fallback Sudden Death music of themes (fallback-sd-music) is now sdmusic.ogg
+ + Make rope stylable by theme: Support for RopeNode.png and rope-step in theme.cfg
+
 Lua API:
+ + New call: SaveMissionVar(varname, value): Save value to mission variable (variable for non-campaign mission)
+ + New call: GetMissionVar(varname): Get value of mission variable
+ + New call: SetTurnTimePaused(isPaused): Call with true to pause turn time, false to unpause
+ + New call: GetTurnTimePaused(): Returns true if turn time is paused due to Lua
+ + New call: AddMissionTeam(color): Add mission team, i.e. the team selected by player in campaign/mission page. Returns <team name>, <team index>
+ + New call: AddMissionHog(health): Add a hedgehog for the mission team
+ + New call: SetTeamPassive(teamname, isPassive): Mark a team as passive. Passive teams do not play and are treated like frozen teams.
+ + New call: IsHogAlive(gear): Returns true if gear is a hegehog which is alive, not about to die and not hidden
+ + New call: SetAmmoSlot(ammoType, slot): Overwrite ammo slot of ammo type (use with care!)
+ + New return value: AddTeam returns <real team name>, <team index>
+ + SetClanColor: Now accepts negative color argument for user clan color, like in AddTeam
+ + AddTeam: Append “_qau” to voicepack name to enable automatic selection of voicepack language
+ + ShowMission: Add new icons: hedgehog (10), flags (11)
+ + Utils library: New calls: getReadableChallengeRecord, updateChallengeRecord, integerSqrt, integerHypotenuse
+ + New callback: onGameResult(winningClan): Called when the game ends normally. winningClan = index of winning clan or -1 on draw
+ + New callback: onCaseDrop(gear): Called at the point where a crate MIGHT be dropped between turns. Gear is the crate gear or nil
+ + New callback: onHogSwitch(oldHog): Called when hog was switched with the “switch hedgehog” utility
+ + SendStat extension: Option to use predefined modes with siPointType: statMessage = "!POINTS", "!TIME", "!TIME0" to "!TIME3", "!CRATES", or "!EMPTY"
+ + SimpleMission: Add isMissionTeam attribute for teams
+ + SpeedShoppa/TargetPractice libraries: Remove custom hog and team info settings
+ + TargetPractice library: Add faceLeft parameter
+ + Params explode, poison in the SpawnFake*Crate functions now optional and default to false
+ + New global: InitHealth: Initial hog health value from game scheme (read-only)
+ + Animate library: AnimOutOfNowhere: destX and destY are now optional (default: current position)
+ * Fix SetClanColor causing crashes and severe rendering bugs
+ * Fix SetAmmoDelay not working properly when called after onGameStart
+ * Fix DismissTeam not clearing team properly
+ * SimpleMission: Fix Lua error spam when a custom goal fails
+ * gstWinner state is preserved after the game ended
+ * If there's a mission team, IsHogLocal now only returns true for hogs in the same clan as the mission team
+
+====================== 0.9.25 ======================
+HIGHLIGHTS:
+ + Complete overhaul of Continental supplies
+ + Can adjust weapon start and crate probabilities in Balanced Random Weapon
+ + Remove rubber duck
+ + New air mine features
+ + Rework team rankings
+ + Tied teams now rank equally
+ + Help button in main menu
+ + 19 new hedgehog taunts
+ + Many new Lua API features
+ * Functionality of controllers restored
+ * Fix at least 2 crashes
+ * Fixed some awkward network bugs which caused games to come to a standstill
+ * Many bugs related to the wrap world edge fixed (but not all)
+ * Sudden Death always came exactly 1 turn later than planned
+
+Game, gameplay:
+ + Increase hedgehog limit to 64
+ + Remove rubber duck
+ + Shotgun, Desert Eagle, Sniper Rifle, Firepunch, Kamikaze, Whip and Baseball Bat can now hit air mines (and some other projectiles)
+ + Freezer can freeze air mines when they don't move too fast
+ + Air mines get stunned by getting shoved
+ + Shotgun shots can now pass through portals
+ * Fix hog being unable to walk after using sniper rifle without firing both shots
+ * Fix sine gun dealing damage to attacker if shooting up
+ * Hedgehog was able to drop more than 2 sticky mines if dropping first one from utility, then stop using utility
+ * Fix Sudden Death starting in the second turn of a round rather than the first
+ * Fix hammer and pickhammer not digging correctly at wrap world edge
+ * Fix drill rocket exploding when digging at bounce/wrap world edge
+ * Fix freezer ray not working through wrap world edge
+ * Fix freezer ray going through bounce world edge
+ * Fix freezer ray extending with low fuel usage when firing straight up/down while holding up/down key
+ * Fix cake walking through bounce world edge
+ * Fix cake walking through land when reaching wrap world edge
+ * Laser sight now works properly through wrap world edge
+ * Fix projectiles behaving incorrectly with land just behind the wrap world edge
+ * Fix bee weapon becoming unusable when hitting attack key in mid-air
+ * Fix hog sometimes getting stuck in land if roping very fast
+ * Fix seduction not stopping if hog took damage before attack was complete
+ * Limit hedgehog health to 268435455 to prevent some bugs
+ * Fix rare possibility that hog is resurrected and starts with 0 or negative health
+
+Game, controls and commands:
+ + Add new key to show mission panel (default: M)
+ + Add new key to cycle through timer values (default: N)
+ + Add default controls for controller (see README.md)
+ + Add chat command “/help”, displays help for chat commands
+ + Rename chat command “/team” to “/clan” (but “/team” still works)
+ * Functionality of controllers restored
+ * Fix crash when 2 or more controllers were connected
+ * Fix cursor teleporting to center after leaving game with a video recording
+ * Fix /hta, /hsa and /hya commands not writing message in chat
+
+Game, audiovisuals:
+ + Campaigns and missions now use the user's chosen custom clan colors
+ + New default brown clan color for better contrast
+ + Allow to change volume during pause
+ + Add sounds: flamethrower, landspray, idle freezer, shorykuen hit
+ + Add taunts: Amazing, Brilliant, Bugger, Cutitout, Drat, Excellent, Fire, Gonnagetyou, Grenade,
+               Hmm, Leavemealone, Ohdear, Ouch, Revenge, Runaway, Solong, Thisoneismine, Whatthe,
+               Watchthis
+ * Enemy/AI hogs now say “Hmm” on turn start instead of vowing for revenge (at least in most voice packs)
+ * Fix extreme amounts of droplets when shooting with minigun into ocean world edge
+ * Fix some flakes disappearing in world wrap worlds while moving camera
+ * Fix invisible projectile timer, attack bar, target on other side of wrap world edge
+ * Fix attack bar drawn over GUI elements
+ * Fix video recorder not working when game audio was disabled
+ * Fix teleport tooltip claiming it doesn't end turn in hog placing phase with inf. attack
+ * Prevent voices from being spoken directly before a victory voice
+ * Fix damage not being displayed if hog drowns in water with 100% opacity (like in Compost theme)
+ * Fix retreat timer appearing after using baseball bat or whip and immediately taking damage
+ * Fix musical effects of RC plane and piano not playing if music is enabled but sounds effects are disabled
+
+Frontend:
+ + Add help button in main menu (link to Hedgewars Wiki)
+ + Add setting to disable audio dampening when losing window focus
+ + Rework player rankings: Losing clans are now ranked in the reverse order they died
+ * Fix player rankings on round draw: Clans that died in the same turn now have the same rank
+ * Fix rare crash when aborting video encoding in progress
+ * Fix critical failure to cleanup teams list after rejoining game under certain conditions
+ * Fix displayed Sudden Death timeout being off by one
+ * Controllers are detected again
+ * Fix failure to shutdown game window properly after player got kicked
+ * No longer allow having schemes with equal names (case-insensitive)
+ * Refuse to load schemes which match the name of a default scheme
+ * No longer save default weapon schemes into file
+ * Pseudo player names in chat (like “[server]”) are no longer clickable
+ * Lobby/room: No longer allow opening context menu if no player selected
+ * Fix game window width/height setting being broken when using Arabic locale
+
+Server:
+ + Add “/help” chat command for command help
+ + Can now clear room greeting by using chat command “/greeting” without arguments
+ + Many new error and status messages for improved usability
+ * Fix many server messages being not translated
+
+Highlander:
+ * Fix all hogs receiving a free teleport after hog placement phase
+ * Fix hogs receiving air strikes in maps with border
+
+Racer/TechRacer:
+ * Fix rare bug in TechRacer causing crates and other objects to not appear on start of turn
+ * Fix ranking of teams if teams are tied
+
+Balanced Random Weapon:
+ + Can adjust weapon start and crate probabilities by using ammo scheme
+
+Random Weapon:
+ * Fix breakage when enabling per-hog ammo
+
+Mutant:
+ + Do not reduce mutant's health in Ready phase
+ + Play poison hurt sound when mutant is low on health
+ + Unlock game scheme
+
+Construction Mode:
+ * Fix girder/rubber cost not being updated correctly after selection
+
+Continental supplies:
+ + Continents are now selected before the game starts
+ + Continents give hog different start health
+ + Add Antarctica special: Upside-Down World (teleport to top of map)
+ + Major rewrite of ALL texts for better usability
+ + Add custom weapon tooltips
+ + Improve audiovisual effects
+ + Show message when hog receives new continent ammo
+ + Sabotaged hedgehogs also emit smoke when it's not their turn
+ + Can switch continent in reverse order with [Precise]+[Switch]
+ * Sabotage deals no damage in ready phase, while attacking or retreating
+ * Invulnerability now protects from sabotage damage
+ * Sabotage kills hog instantly when health reaches 0
+ * Reliably prevent using of Lonely Cries and baseball bat specials when usage not allowed
+ * Don't explode Anno 1302, Medicine and Bouncy Boomerang if drowning
+ * Don't play “Missed” and “Laugh” taunt when those don't make sense
+ * Fix retreat timer not turning red for some weapons
+
+Space Invasion:
+ + Display round score in a separate row
+ + Keep round score displayed after round ends, remove round score announcer message
+ + If team scores are tied at the end, continue playing rounds with the tied teams until there's a winner
+ * Fix rare Lua error message spam at end of game
+ * Fix round score and other info numbers messing up after screen resize
+ * Fix kamikaze bonus not being shown
+
+Missions and styles:
+ * Basic Movement Training: Back jumps are now easier
+ * The Great Escape: Infinite attack mode did not work
+ * Shotgun/Sniper Rifle Target Practice: Suppress “X remaining” message
+ * Fix resurrection animation appearing at wrong position for some missions and styles
+ * Fix Lua error when playing any mission or style in Lithuanian language
+
+A Classic Fairytale:
+ * Fix clan membership of princess in some missions
+ * Mission 5: Tribe was not in same clan as Natives, screwing up stats a bit
+
+A Space Adventure:
+ + The big bang: Terrain types are easier to distinguish
+ + Hard Flying: Display current flying time next to team bar
+ * Hard Flying: Fix incorrect recorded time, was 6 seconds more than reality
+ * Searching in the Dust: Fix display error when destroying device crate
+ * Searching in the Dust: Don't take away control right above the pit near Sandy
+ * The big bang: Don't say "Missed" or "Yes, Sir!" when inappropriate
+ * The last Encounter: Fix clan membership of PAotH
+
+Themes:
+ + New Sudden Death water texture for CrazyMission theme
+ + Add dust flakes for Cheese and CrazyMission themes
+ + New land objects for Beach theme
+ * Fix repeating sun in Hoggywood theme
+
+Content creation:
+ + HWPs can be nested inside HWPs (1 layer deep only)
+ + Add-ons now support preview images for campaign missions
+
+Translations:
+ + Translations kept up-to-date: German, Polish
+ + Major translation updates: Russian, Japanese, Scottish Gaelic, Ukrainian
+
+Lua API:
+ * Deprecation: Setting TurnTimeLeft/ReadyTimeLeft directly is deprecated and will become useless in future. Use the setter functions below
+ * Deprecation: Symbols amDuck/gtDuck are deprecated, will be removed later. For now, they alias to amCreeper/gtCreeper
+ * Changed global: lfCurrentHog becomes lfCurHogCrate
+ + New call: SetTurnTimeLeft(newTurnTimeLeft): Set remaining turn time
+ + New call: SetReadyTimeLeft(newReadyTimeLeft): Set remaining ready time
  + New call: Retreat(time [, respectGetAwayTimeFactor): Force current turn into retreating mode
+ + New call: GetAmmoTimer(gearUid, ammoType): Returns current set timer for given ammoType and hog gear in ms. Returns nil for non-timerable ammo
+ + New call: EnableSwitchHog(): Enable hog switching
+ + New call: GetAmmo(ammoType): Returns ammo configuration (corresponds to SetAmmo)
+ + New call: GetVampiric(): Returns true if vampirism is currently active
+ + New call: GetLaserSight(): Returns true if laser sight (as utility) is currenctly active (ignoring sniper rifle)
+ + New call: IsHogHidden(gear): Returns true if hog is hidden
+ + New call: PlayMusicSound(soundId): Play a sound as replacement for the main background music
+ + New call: StopMusicSound(soundId): Stop a “music sound” and resume the regular music
+ + Changed call: AddTeam: 2nd param. color: Accepts negative value to use a default clan color from player settings
+ + Changed call: HedgewarsScriptLoad: 2nd param. mustExist. If false, it's allowed for the script to not exist
+ + Changed call: HedgewarsScriptLoad: Return true on success and false on failure
+ + Change callback: onGearResurrect: 2nd parameter for visual gear spawned at resurrect position (might be nil)
+ + 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
+ + Can set custom team rank: Call SendStat with 1st param siTeamRank and 2nd param to the desired rank, as string. Must be called before siPlayerKills of the team for which this applies
+ + New Lua library: Achievements (currently only for internal use)
+ + Add sprite tint globals in Utilts library: U_LAND_TINT_NORMAL, U_LAND_TINT_ICE, U_LAND_TINT_INDESTRUCTIBLE and U_LAND_TINT_BOUNCY
+ + New globals: Infinite fly time for jetpack/Birdy by setting health to JETPACK_FUEL_INFINITE or BIRDY_ENERGY_INFINITE, respectively
+ + New global game variable: MaxCaseDrops. Max. number of crats which can be in game by crate drops
+ + New global: NO_CURSOR. Value of CursorX and CursorY if cursor is inactive
+ + New global: AMMO_INFINITE. Value for infinite ammo count for AddAmmo and other functions
+ + New global: MAX_HOG_HEALTH. Maximum possible hedgehog health
+ + New global: MAX_TURN_TIME. Maximum possible turn time
+ + New global: EXPLForceDraw. Flag for Explode function, forces land removal even with gfSolidLand on
+ + New global: INTERFACE. Type of the game interface: "desktop" for desktop, "touch" for touchscreen
+ + New globals: capcolDefault, capcolSetting: Default caption colors
+ * Animate library: Remove defunct follow argument for AnimVisualGear
+ * Fixed variable: TotalRounds was -1 (instead of 0) in first real round after hog placement phase
+ * Fixed variables: LeftX, RightX, TopY, LAND_WIDTH, LAND_HEIGHT were broken if onVisualGearAdd was defined
+ * AI sometimes intentionally shot hedgehogs with aihDoesntMatter set
 
 ====================== 0.9.24.1 ====================
  * Fix crash when portable portal device is fired at reduced graphics quality
@@ -91,6 +512,7 @@
  * Capture the Flag: Fix many bugs caused by playing with >2 teams
  * Capture the Flag: Properly place flag when first hog uses kamikaze or TimeBox
  * Capture the Flag: Fix flag not being dropped when carrier uses piano strike
+ * Capture the Flag: Fix clan not winning if enemy was in time travel
  * CTF_Blizzard: Don't allow more than 2 clans. Excess hogs will be removed
 
 A Space Adventure:
--- a/INSTALL.md	Wed May 16 18:22:28 2018 +0200
+++ b/INSTALL.md	Wed Jul 31 23:14:27 2019 +0200
@@ -7,6 +7,8 @@
 
 Dependencies
 ------------
+### Hardware dependencies
+See README.md.
 
 ### Core dependencies
 
@@ -22,6 +24,9 @@
 - SDL\_mixer >= 2.0
 - SDL\_image >= 2.0
 - SDL\_ttf >= 2.0
+- PhysFS >= 3.0.0
+
+On FreeBSD, you also need the package “fpc-rtl-extra”.
 
 ### Recommended optional dependencies
 
@@ -29,8 +34,6 @@
 usually better to have them installed. Hedgewars has fallback mechanisms
 in if these are not found on your system.
 
-- qtstyleplugins (for Qt 5)
-- PhysFS >= 2.0.0
 - Lua = 5.1.0
 
 ### Optional dependencies
@@ -45,9 +48,6 @@
     - GHC >= 6.10
     - Various Haskell packages (see below)
 
-PhysFS will be internally built if `-DPHYSFS_SYSTEM=OFF` is passed to `cmake`
-(also allows to set `PHYSFS_LIBRARY` and `PHYSFS_INCLUDE_DIR` if needed).
-
 Lua will be automatically built if not found.
 
 ### Hedgewars Server dependencies
@@ -77,9 +77,9 @@
     - `utf8-string`
     - `SHA`
     - `entropy`
-    - `zlib` >= 0.5.3 and < 0.6
+    - `zlib` >= 0.5.3 and < 0.7
     - `regex-tdfa`
-
+    - `binary` >= 0.8.5.1
 
 Building
 --------
@@ -118,7 +118,6 @@
 - `CMAKE_INSTALL_PREFIX`: Installation directory
 - `NOSERVER`: Set to `ON` to *not* build the server
 - `NOVIDEOREC`: Set to `ON` to *not* build the video recorder
-- `SYSTEM_PHYSFS`: Set to `OFF` to use Hedgewars-bundled PhysFS
 
 ### Step 2: Make
 
@@ -154,15 +153,6 @@
 If this didn't work, make sure you have the correct Qt version
 (see above).
 
-### Hedgewars compiles successfully, but games instantly crash the map preview fails
-
-This is likely to be a problem with PhysFS. Try to build Hedgewars
-with the Hedgewars-bundled PhysFS by setting the CMake option
-`SYSTEM_PHYSFS=OFF`, then try to run `make` again.
-
-If the _bundled_ PhysFS fails, too, this is likely to be a bug in
-Hedgewars, please report at <https://issues.hedgewars.org/>.
-
 ### Broken/missing Haskell dependencies
 
 First, try to obtain the missing Haskell packages and make sure GHC
--- a/QTfrontend/CMakeLists.txt	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/CMakeLists.txt	Wed Jul 31 23:14:27 2019 +0200
@@ -45,6 +45,37 @@
 list(APPEND locsout ${firstline} "\n}\\;\n")
 file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/servermessages.h ${locsout})
 
+
+# Credits localization
+file(GLOB CreditsCSV ${CMAKE_SOURCE_DIR}/QTfrontend/res/credits.csv)
+foreach(csvfile ${CreditsCSV})
+    # Load credits.csv
+    file(READ ${csvfile} csv)
+
+    # Match first line of CSV file
+    string(REGEX MATCH "(E|S|U),\"[^\n\"]+\"" loc_top ${csv})
+    string(REGEX REPLACE "(E|S|U),\"([^\n\"]+)\"" "\nQT_TRANSLATE_NOOP(\"credits\", \"\\2\")" s ${loc_top})
+    list(APPEND csvlocs ${s})
+
+    # Match remaining lines of CSV file
+    string(REGEX MATCHALL "\n(E|S|U),\"[^\n\"]+\"" locs ${csv})
+    foreach(str ${locs})
+        string(REGEX REPLACE "(E|S|U),\"([^\n\"]+)\"" "QT_TRANSLATE_NOOP(\"credits\", \"\\2\")" s ${str})
+        list(APPEND csvlocs ${s})
+    endforeach(str)
+endforeach(csvfile)
+
+list(REMOVE_DUPLICATES csvlocs)
+list(GET csvlocs 0 firstline)
+list(REMOVE_AT csvlocs 0)
+set(locsout "const char * creditsMessages[] = {")
+foreach(l ${csvlocs})
+    list(APPEND locsout ${l} ",")
+endforeach(l)
+list(APPEND locsout ${firstline} "\n}\\;\n")
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/creditsmessages.h ${locsout})
+
+
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/model)
@@ -92,8 +123,10 @@
     main.cpp
     team.cpp
     campaign.cpp
+    mission.cpp
     ui_hwform.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/hwconsts.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/sdlkeys.cpp
     )
 
 if(MINGW)
@@ -136,7 +169,9 @@
     hwconsts.h
     sdlkeys.h
     campaign.h
+    mission.h
     ${CMAKE_CURRENT_BINARY_DIR}/servermessages.h
+    ${CMAKE_CURRENT_BINARY_DIR}/creditsmessages.h
     )
 
 set(hwfr_rez hedgewars.qrc)
@@ -214,6 +249,13 @@
 
 if(CMAKE_CXX_COMPILER MATCHES "clang*")
     list(APPEND HW_LINK_LIBS stdc++ m)
+    if(NOT APPLE)
+        list(APPEND HW_LINK_LIBS atomic)
+    endif()
+endif()
+
+if(WIN32 AND VCPKG_TOOLCHAIN)
+    list(APPEND HW_LINK_LIBS Qt5::WinMain)
 endif()
 
 target_link_libraries(hedgewars ${HW_LINK_LIBS})
--- a/QTfrontend/HWApplication.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/HWApplication.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -90,6 +90,7 @@
             form->NetConnectQuick(address, (quint16) port);
             return true;
         } else {
+            //: Here, “scheme” refers to the scheme of a Uniform Resource Identifier”
             const QString errmsg = tr("Scheme '%1' not supported").arg(scheme);
             MessageDialog::ShowErrorMessage(errmsg, form);
             return false;
--- a/QTfrontend/binds.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/binds.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -25,9 +25,13 @@
     {"+right",    "right",      QT_TRANSLATE_NOOP("binds", "right"),           NULL, NULL},
     {"+down",     "down",       QT_TRANSLATE_NOOP("binds", "down"),            NULL, NULL},
     {"+precise",  "left_shift", QT_TRANSLATE_NOOP("binds", "precise aim"),     NULL, NULL},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "hold down precise"), QT_TRANSLATE_NOOP("binds", "stand still on slippery land"), NULL, NULL},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "precise + left/right"), QT_TRANSLATE_NOOP("binds", "change direction without moving"), NULL, NULL},
     {"ljump",     "return",     QT_TRANSLATE_NOOP("binds", "long jump"),       NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Traverse gaps and obstacles by jumping:")},
     {"hjump",     "backspace",  QT_TRANSLATE_NOOP("binds", "high jump"),       NULL, NULL},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "high jump (twice)"), QT_TRANSLATE_NOOP("binds", "backwards jump"), NULL, NULL},
     {"switch",    "tab",        QT_TRANSLATE_NOOP("binds", "switch"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Switch your currently active hog (if possible):")},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "precise + switch"), QT_TRANSLATE_NOOP("binds", "switch backwards"), NULL, NULL},
     {"ammomenu",  "mouser",     QT_TRANSLATE_NOOP("binds", "ammo menu"),       QT_TRANSLATE_NOOP("binds (categories)", "Weapons"), QT_TRANSLATE_NOOP("binds (descriptions)", "Pick a weapon or utility item:")},
     {"slot 1",    "f1",         QT_TRANSLATE_NOOP("binds", "slot 1"),          NULL, NULL},
     {"slot 2",    "f2",         QT_TRANSLATE_NOOP("binds", "slot 2"),          NULL, NULL},
@@ -39,11 +43,14 @@
     {"slot 8",    "f8",         QT_TRANSLATE_NOOP("binds", "slot 8"),          NULL, NULL},
     {"slot 9",    "f9",         QT_TRANSLATE_NOOP("binds", "slot 9"),          NULL, NULL},
     {"slot :",    "f10",        QT_TRANSLATE_NOOP("binds", "slot 10"),         NULL, NULL},
+    {"setweap ~", "none",       QT_TRANSLATE_NOOP("binds", "unselect weapon"), NULL, NULL},
     {"timer 1",   "1",          QT_TRANSLATE_NOOP("binds", "timer 1 sec"),     NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Set the timer on bombs and timed weapons:")},
     {"timer 2",   "2",          QT_TRANSLATE_NOOP("binds", "timer 2 sec"),     NULL, NULL},
     {"timer 3",   "3",          QT_TRANSLATE_NOOP("binds", "timer 3 sec"),     NULL, NULL},
     {"timer 4",   "4",          QT_TRANSLATE_NOOP("binds", "timer 4 sec"),     NULL, NULL},
     {"timer 5",   "5",          QT_TRANSLATE_NOOP("binds", "timer 5 sec"),     NULL, NULL},
+    {"timer_u",   "n",          QT_TRANSLATE_NOOP("binds", "change timer"),    NULL, NULL},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "precise + timer"), QT_TRANSLATE_NOOP("binds", "change bounciness"), NULL, NULL},
     {"+attack",   "space",      QT_TRANSLATE_NOOP("binds", "attack"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Fire your selected weapon or trigger an utility item:")},
     {"put",       "mousel",     QT_TRANSLATE_NOOP("binds", "put"),             NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pick a weapon or a target location under the cursor:")},
     {"findhh",    "h",          QT_TRANSLATE_NOOP("binds", "autocam / find hedgehog"),QT_TRANSLATE_NOOP("binds (categories)", "Camera"), QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle automatic camera / refocus on active hedgehog:")},
@@ -54,9 +61,10 @@
 //  {"+cur_m",    "",           QT_TRANSLATE_NOOP("binds", "movement key modifier"),    NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Specify a modifier key to move camera and cursor using your default hog movement keys:")},
     {"zoomin",    "wheelup",    QT_TRANSLATE_NOOP("binds", "zoom in"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Modify the camera's zoom level:")},
     {"zoomout",   "wheeldown",  QT_TRANSLATE_NOOP("binds", "zoom out"),        NULL, NULL},
-    {"zoomreset", "mousem",     QT_TRANSLATE_NOOP("binds", "reset zoom"),      NULL, NULL},
-    {"chat",      "t",          QT_TRANSLATE_NOOP("binds", "chat"),            QT_TRANSLATE_NOOP("binds (categories)", "Miscellaneous"), QT_TRANSLATE_NOOP("binds (descriptions)", "Talk to your team or all participants:")},
-    {"chat team", "u",          QT_TRANSLATE_NOOP("binds", "team chat"),       NULL, NULL},
+    {"zoomreset", "mousem",     QT_TRANSLATE_NOOP("binds", "reset zoom to start value"), NULL, NULL},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "precise + reset zoom"), QT_TRANSLATE_NOOP("binds", "set zoom to 100%"), NULL, NULL},
+    {"chat",      "t",          QT_TRANSLATE_NOOP("binds", "chat"),            QT_TRANSLATE_NOOP("binds (categories)", "Miscellaneous"), QT_TRANSLATE_NOOP("binds (descriptions)", "Talk to your clan or all participants:")},
+    {"chat team", "u",          QT_TRANSLATE_NOOP("binds", "clan chat"),       NULL, NULL},
     {"history",   "`",          QT_TRANSLATE_NOOP("binds", "chat history"),    NULL, NULL},
     {"pause",     "p",          QT_TRANSLATE_NOOP("binds", "pause / auto skip"),NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pause, continue or leave your game:")},
     {"quit",      "escape",     QT_TRANSLATE_NOOP("binds", "quit"),            NULL, NULL},
@@ -65,11 +73,18 @@
     {"+volup",    "0",          QT_TRANSLATE_NOOP("binds", "volume up"),       NULL, NULL},
     {"mute",      "8",          QT_TRANSLATE_NOOP("binds", "mute audio"),      NULL, NULL},
     {"fullscr",   "f12",        QT_TRANSLATE_NOOP("binds", "change mode"),     NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle fullscreen mode:")},
-    {"capture",   "c",          QT_TRANSLATE_NOOP("binds", "capture"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Take a screenshot:")},
-    {"+speedup",  "s",          QT_TRANSLATE_NOOP("binds", "speed up replay"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Demo replay:")},
+    {"capture",   "c",          QT_TRANSLATE_NOOP("binds", "screenshot"),      NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Take a screenshot:")},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "precise + screenshot"), QT_TRANSLATE_NOOP("binds", "save map as image"), NULL, NULL},
+    {"+speedup",  "f",          QT_TRANSLATE_NOOP("binds", "speed up replay"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Demo replay:")},
+    {"+mission",  "m",          QT_TRANSLATE_NOOP("binds", "show mission information"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Heads-up display:")},
+    {"gearinfo",  "o",          QT_TRANSLATE_NOOP("binds", "show object information"), NULL, NULL},
     //: This refers to the team info bars (name/flag/health) of all teams. These are shown at the bottom center of the screen
-    {"rotmask",   "delete",     QT_TRANSLATE_NOOP("binds", "toggle team bars"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Heads-up display:")},
+    {"rotmask",   "delete",     QT_TRANSLATE_NOOP("binds", "toggle team bars"), NULL, NULL},
     {"rottags",   "home",       QT_TRANSLATE_NOOP("binds", "toggle hedgehog tags"), NULL, NULL},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "precise + toggle hedgehog tags"), QT_TRANSLATE_NOOP("binds", "change hedgehog tag types"), NULL, NULL},
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "switch + toggle hedgehog tags"), QT_TRANSLATE_NOOP("binds", "toggle hedgehog tag translucency"), NULL, NULL},
+
+    {"!MULTI",    QT_TRANSLATE_NOOP("binds (combination)", "precise + switch + toggle hedgehog tags"), QT_TRANSLATE_NOOP("binds", "toggle HUD"), NULL, NULL},
 #ifdef VIDEOREC
     {"record",    "r",          QT_TRANSLATE_NOOP("binds", "record"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Record video:")}
 #endif
--- a/QTfrontend/binds.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/binds.h	Wed Jul 31 23:14:27 2019 +0200
@@ -22,9 +22,9 @@
 #include <QString>
 
 #ifdef VIDEOREC
-#define BINDS_NUMBER 49
+#define BINDS_NUMBER 63
 #else
-#define BINDS_NUMBER 48
+#define BINDS_NUMBER 62
 #endif
 
 struct BindAction
--- a/QTfrontend/campaign.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/campaign.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -31,7 +31,7 @@
     // if then is found rename it to use _
     QString spaceCampName = campaignName;
     spaceCampName = spaceCampName.replace(QString("_"),QString(" "));
-    if (!teamfile->childGroups().contains("Campaign " + campaignName) and
+    if (!teamfile->childGroups().contains("Campaign " + campaignName) &&
             teamfile->childGroups().contains("Campaign " + spaceCampName)){
         teamfile->beginGroup("Campaign " + spaceCampName);
         QStringList keys = teamfile->childKeys();
@@ -52,12 +52,20 @@
     missionInList: QComboBox index of the mission as selected in the mission widget
     teamName: Name of the playing team
 */
-bool isMissionWon(QString & campaignName, int missionInList, QString & teamName)
+bool isCampMissionWon(QString & campaignName, int missionInList, QString & teamName)
 {
     QSettings* teamfile = getCampTeamFile(campaignName, teamName);
     int progress = teamfile->value("Campaign " + campaignName + "/Progress", 0).toInt();
     int unlockedMissions = teamfile->value("Campaign " + campaignName + "/UnlockedMissions", 0).toInt();
-    if(progress>0 and unlockedMissions==0)
+    // The CowardMode cheat unlocks all campaign missions,
+    // but as "punishment", none of them will be marked as completed.
+    // Added to make it easier to test campaigns.
+    bool cheat = teamfile->value("Team/CowardMode", false).toBool();
+    if(cheat)
+    {
+        return false;
+    }
+    else if(progress>0 && unlockedMissions==0)
     {
         QSettings campfile("physfs://Missions/Campaign/" + campaignName + "/campaign.ini", QSettings::IniFormat, 0);
         campfile.setIniCodec("UTF-8");
@@ -79,7 +87,8 @@
 {
     QSettings* teamfile = getCampTeamFile(campaignName, teamName);
     bool won = teamfile->value("Campaign " + campaignName + "/Won", false).toBool();
-    return won;
+    bool cheat = teamfile->value("Team/CowardMode", false).toBool();
+    return won && !cheat;
 }
 
 QSettings* getCampMetaInfo()
@@ -118,15 +127,20 @@
 
     int progress = teamfile->value("Campaign " + campaignName + "/Progress", 0).toInt();
     int unlockedMissions = teamfile->value("Campaign " + campaignName + "/UnlockedMissions", 0).toInt();
+    bool cheat = teamfile->value("Team/CowardMode", false).toBool();
 
     QSettings campfile("physfs://Missions/Campaign/" + campaignName + "/campaign.ini", QSettings::IniFormat, 0);
     campfile.setIniCodec("UTF-8");
 
     QSettings* m_info = getCampMetaInfo();
 
-    if(progress>=0 and unlockedMissions==0)
+    if(cheat)
     {
-        for(unsigned int i=progress+1;i>0;i--)
+        progress = campfile.value("MissionNum", 1).toInt();
+    }
+    if((progress >= 0 && unlockedMissions == 0) || cheat)
+    {
+        for(unsigned int i = progress + 1; i > 0; i--)
         {
             MissionInfo missionInfo;
             QString script = campfile.value(QString("Mission %1/Script").arg(i)).toString();
@@ -137,8 +151,8 @@
                 missionInfo.realName = m_info->value(scriptPrefix+".name", missionInfo.name).toString();
                 missionInfo.description = m_info->value(scriptPrefix + ".desc",
                                             QObject::tr("No description available")).toString();
-                QString image = campfile.value(QString("Mission %1/Script").arg(i)).toString().replace(QString(".lua"),QString(".png"));
-                missionInfo.image = ":/res/campaign/"+campaignName+"/"+image;
+                QString image = campfile.value(QString("Mission %1/Script").arg(i)).toString().replace(QString(".lua"),QString("@2x.png"));
+                missionInfo.image = "physfs://Graphics/Missions/Campaign/"+campaignName+"/"+image;
                 if (!QFile::exists(missionInfo.image))
                     missionInfo.image = ":/res/CampaignDefault.png";
                 missionInfoList.append(missionInfo);
@@ -159,8 +173,8 @@
             missionInfo.realName = m_info->value(scriptPrefix+".name", missionInfo.name).toString();
             missionInfo.description = m_info->value(scriptPrefix + ".desc",
                                             QObject::tr("No description available")).toString();
-            QString image = campfile.value(QString("Mission %1/Script").arg(missionNumber)).toString().replace(QString(".lua"),QString(".png"));
-            missionInfo.image = ":/res/campaign/"+campaignName+"/"+image;
+            QString image = campfile.value(QString("Mission %1/Script").arg(missionNumber)).toString().replace(QString(".lua"),QString("@2x.png"));
+            missionInfo.image = "physfs://Graphics/Missions/Campaign/"+campaignName+"/"+image;
             if (!QFile::exists(missionInfo.image))
                 missionInfo.image = ":/res/CampaignDefault.png";
             missionInfoList.append(missionInfo);
--- a/QTfrontend/campaign.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/campaign.h	Wed Jul 31 23:14:27 2019 +0200
@@ -36,7 +36,7 @@
 QSettings* getCampTeamFile(QString & campaignName, QString & teamName);
 QSettings* getCampMetaInfo();
 bool isCampWon(QString & campaignName, QString & teamName);
-bool isMissionWon(QString & campaignName, int missionInList, QString & teamName);
+bool isCampMissionWon(QString & campaignName, int missionInList, QString & teamName);
 QString getRealCampName(const QString & campaignName);
 QList<MissionInfo> getCampMissionList(QString & campaignName, QString & teamName);
 
--- a/QTfrontend/drawmapscene.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/drawmapscene.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -29,6 +29,10 @@
 #define DRAWN_MAP_COLOR_CURSOR_PEN (Qt::green)
 #define DRAWN_MAP_COLOR_CURSOR_ERASER (Qt::red)
 
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
 template <class T> T sqr(const T & x)
 {
     return x*x;
--- a/QTfrontend/game.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/game.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -29,6 +29,8 @@
 
 #include "hwform.h"
 #include "ui/page/pageoptions.h"
+#include "ui/page/pagetraining.h"
+#include "ui/page/pagecampaign.h"
 #include "game.h"
 #include "hwconsts.h"
 #include "gameuiconfig.h"
@@ -44,15 +46,14 @@
 // last game info
 QList<QVariant> lastGameStartArgs = QList<QVariant>();
 GameType lastGameType = gtNone;
-QString lastTrainingSubFolder = NULL;
 GameCFGWidget * lastGameCfg = NULL;
 QString lastGameAmmo = NULL;
 TeamSelWidget * lastGameTeamSel = NULL;
 
-QString training, campaign, campaignScript, campaignTeam; // TODO: Cleaner solution?
+QString trainingName, trainingScript, trainingTeam, campaign, campaignScript, campaignTeam; // TODO: Cleaner solution?
 
 HWGame::HWGame(GameUIConfig * config, GameCFGWidget * gamecfg, QString ammo, TeamSelWidget* pTeamSelWidget) :
-    TCPBase(true, 0),
+    TCPBase(true, !config->language().isEmpty(), 0),
     ammostr(ammo),
     m_pTeamSelWidget(pTeamSelWidget)
 {
@@ -75,22 +76,29 @@
 
 void HWGame::onClientDisconnect()
 {
-    switch (gameType)
+    if (demoIsPresent)
     {
-        case gtDemo:
-            // for video recording we need demo anyway
-            emit HaveRecord(rtNeither, demo);
-            break;
-        case gtNet:
-            emit HaveRecord(rtDemo, demo);
-            break;
-        default:
-            if (gameState == gsInterrupted || gameState == gsHalted)
-                emit HaveRecord(rtSave, demo);
-            else if (gameState == gsFinished)
+        switch (gameType)
+        {
+            case gtDemo:
+                // for video recording we need demo anyway
+                emit HaveRecord(rtNeither, demo);
+                break;
+            case gtNet:
                 emit HaveRecord(rtDemo, demo);
-            else
-                emit HaveRecord(rtNeither, demo);
+                break;
+            default:
+                if (gameState == gsInterrupted || gameState == gsHalted)
+                    emit HaveRecord(rtSave, demo);
+                else if (gameState == gsFinished)
+                    emit HaveRecord(rtDemo, demo);
+                else
+                    emit HaveRecord(rtNeither, demo);
+        }
+    }
+    else
+    {
+        emit HaveRecord(rtNeither, demo);
     }
     SetGameState(gsStopped);
 }
@@ -139,37 +147,196 @@
 
 void HWGame::SendQuickConfig()
 {
+    /* Load and increase Quick Game experience level.
+    Experience increases by 1 for each started game and maxes out
+    at 20. Low experience levels will introduce a "beginner's bias" to make
+    the first quick games easier and simpler. The max. possible difficulty
+    increases progressively the longer you play.
+    If experience is maxed out, the beginner's bias is gone and quick games
+    are completely random. */
+    int exp = config->quickGameExperience();
+    if(exp < 20)
+    {
+       config->setQuickGameExperience(exp + 1);
+    }
+    qDebug("Starting quick game ...");
+    qDebug("Quick Game experience level: %d", exp);
+
+    // Init stuff
     QByteArray teamscfg;
     QAbstractItemModel * themeModel = DataManager::instance().themeModel()->withoutHidden();
 
     HWProto::addStringToBuffer(teamscfg, "TL");
-    HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
-                               .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount(), 0).data(ThemeModel::ActualNameRole).toString() : "Nature"));
+
+    // Random seed
     HWProto::addStringToBuffer(teamscfg, "eseed " + QUuid::createUuid().toString());
 
-    HWProto::addStringToBuffer(teamscfg, "e$template_filter 2");
-    HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
+    int r, minhogs, maxhogs;
 
+    // Random map type
+    r = rand() % 10000;
+    if(r < 3000) { // 30%
+        // Random
+        r = 0;
+    } else if(r < 5250) { // 22.5%
+        // Maze
+        if(exp <= 3)
+            r = 0;
+        else
+            r = 1;
+    } else if(r < 7490) { // 22.4%
+        // Perlin
+        if(exp <= 7)
+            r = 1;
+        else
+            r = 2;
+    } else if(r < 7500 && exp >= 5) { // 0.1%
+        // Floating Flowers (just for fun)
+        r = 5;
+    } else if(r < 8750) { // 12.5%
+        // Image map
+        r = 3;
+    } else { // 12.5%
+        // Forts
+        r = 4;
+    }
+    switch(r)
+    {
+        // Random map
+        default:
+        case 0: {
+            r = rand() % 3;
+            if(r == 0)
+            {
+                // small island
+                HWProto::addStringToBuffer(teamscfg, "e$template_filter 1");
+                minhogs = 3;
+                maxhogs = 4;
+            }
+            else if(r == 1 || exp <= 6)
+            {
+                // medium island
+                HWProto::addStringToBuffer(teamscfg, "e$template_filter 2");
+                minhogs = 4;
+                maxhogs = 5;
+            }
+            else
+            {
+                // cave (locked at low experience because these maps can be huge)
+                HWProto::addStringToBuffer(teamscfg, "e$template_filter 4");
+                minhogs = 4;
+                maxhogs = 6;
+            }
+            HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
+            break;
+        }
+        // Maze
+        case 1: {
+            minhogs = 4;
+            maxhogs = 6;
+            HWProto::addStringToBuffer(teamscfg, "e$mapgen 1");
+            HWProto::addStringToBuffer(teamscfg, "e$template_filter "+QString::number(rand()%6));
+            HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%16+6));
+            break;
+        }
+        // Perlin
+        case 2: {
+            minhogs = 4;
+            maxhogs = 6;
+            HWProto::addStringToBuffer(teamscfg, "e$mapgen 2");
+            HWProto::addStringToBuffer(teamscfg, "e$template_filter "+QString::number(rand()%6));
+            HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
+            break;
+        }
+        // Image map
+        case 3: {
+            minhogs = 4;
+            maxhogs = 6;
+            HWProto::addStringToBuffer(teamscfg, "e$mapgen 3");
+            // Select map from hardcoded list.
+            // TODO: find a more dynamic solution.
+            r = rand() % cQuickGameMaps.count();
+            HWProto::addStringToBuffer(teamscfg, "e$map " + cQuickGameMaps[r]);
+            break;
+        }
+        // Forts
+        case 4: {
+            minhogs = 4;
+            maxhogs = 6;
+            HWProto::addStringToBuffer(teamscfg, "e$mapgen 4");
+            HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%20+1));
+            break;
+        }
+        // Floating Flowers
+        // (actually empty map; this forces the engine to generate fallback structures to have
+        // something for hogs to stand on)
+        case 5: {
+            minhogs = 4;
+            maxhogs = 8;
+            HWProto::addStringToBuffer(teamscfg, "e$mapgen 3");
+            HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%4+3));
+            break;
+        }
+    }
+
+    // Theme
+    HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
+        .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount(), 0).data(ThemeModel::ActualNameRole).toString() : "Nature"));
+
+    int hogs = minhogs + rand() % (maxhogs-minhogs+1);
+    // Cap hog count at low experience
+    if((exp <= 8) && (hogs > 5))
+        hogs = 5;
+    else if((exp <= 5) && (hogs > 4))
+        hogs = 4;
+
+    // Teams
+    // Player team
     HWTeam team1;
     team1.setDifficulty(0);
     team1.setColor(0);
-    team1.setNumHedgehogs(4);
+    team1.setNumHedgehogs(hogs);
     HWNamegen::teamRandomEverything(team1);
-    team1.setVoicepack("Default");
-    HWProto::addStringListToBuffer(teamscfg,
-                                   team1.teamGameConfig(100));
+    team1.setVoicepack("Default_qau");
 
+    // Computer team
     HWTeam team2;
-    team2.setDifficulty(4);
+    // Random difficulty.
+    // Max. possible difficulty is capped at low experience levels.
+    if(exp >= 15) // very easy to very hard (full range)
+        r = 5 - rand() % 5;
+    else if(exp >= 9) // very easy to hard
+        r = 5 - rand() % 4;
+    else if(exp >= 6) // very easy to medium
+        r = 5 - rand() % 3;
+    else if(exp >= 2) // very easy to easy
+        r = 5 - rand() % 2;
+    else // very easy
+        r = 5;
+    team2.setDifficulty(r);
     team2.setColor(1);
-    team2.setNumHedgehogs(4);
+    team2.setNumHedgehogs(hogs);
+    // Make sure the team names are not equal
     do
         HWNamegen::teamRandomEverything(team2);
     while(!team2.name().compare(team1.name()) || !team2.hedgehog(0).Hat.compare(team1.hedgehog(0).Hat));
-    team2.setVoicepack("Default");
-    HWProto::addStringListToBuffer(teamscfg,
-                                   team2.teamGameConfig(100));
+    team2.setVoicepack("Default_qau");
 
+    // Team play order
+    r = rand() % 2;
+    if(r == 0 || exp <= 4) // player plays first
+    {
+        HWProto::addStringListToBuffer(teamscfg, team1.teamGameConfig(100));
+        HWProto::addStringListToBuffer(teamscfg, team2.teamGameConfig(100));
+    }
+    else // computer plays first
+    {
+        HWProto::addStringListToBuffer(teamscfg, team2.teamGameConfig(100));
+        HWProto::addStringListToBuffer(teamscfg, team1.teamGameConfig(100));
+    }
+
+    // Ammo scheme "Default"
+    // TODO: Random schemes
     HWProto::addStringToBuffer(teamscfg, QString("eammloadt %1").arg(cDefaultAmmoStore->mid(0, cAmmoNumber)));
     HWProto::addStringToBuffer(teamscfg, QString("eammprob %1").arg(cDefaultAmmoStore->mid(cAmmoNumber, cAmmoNumber)));
     HWProto::addStringToBuffer(teamscfg, QString("eammdelay %1").arg(cDefaultAmmoStore->mid(2 * cAmmoNumber, cAmmoNumber)));
@@ -184,8 +351,16 @@
 {
     QByteArray traincfg;
     HWProto::addStringToBuffer(traincfg, "TL");
+
+    HWTeam missionTeam = HWTeam();
+    missionTeam.setName(config->Form->ui.pageTraining->CBTeam->currentText());
+    missionTeam.loadFromFile();
+    missionTeam.setNumHedgehogs(HEDGEHOGS_PER_TEAM);
+    missionTeam.setMissionTeam(true);
+    HWProto::addStringListToBuffer(traincfg, missionTeam.teamGameConfig(100));
+
     HWProto::addStringToBuffer(traincfg, "eseed " + QUuid::createUuid().toString());
-    HWProto::addStringToBuffer(traincfg, "escript " + training);
+    HWProto::addStringToBuffer(traincfg, "escript " + trainingScript);
 
     RawSendIPC(traincfg);
 }
@@ -194,8 +369,15 @@
 {
     QByteArray campaigncfg;
     HWProto::addStringToBuffer(campaigncfg, "TL");
+
+    HWTeam missionTeam = HWTeam();
+    missionTeam.setName(config->Form->ui.pageCampaign->CBTeam->currentText());
+    missionTeam.loadFromFile();
+    missionTeam.setNumHedgehogs(HEDGEHOGS_PER_TEAM);
+    missionTeam.setMissionTeam(true);
+    HWProto::addStringListToBuffer(campaigncfg, missionTeam.teamGameConfig(100));
+
     HWProto::addStringToBuffer(campaigncfg, "eseed " + QUuid::createUuid().toString());
-
     HWProto::addStringToBuffer(campaigncfg, "escript " + campaignScript);
 
     RawSendIPC(campaigncfg);
@@ -277,6 +459,11 @@
             SetGameState(gsFinished);
             break;
         }
+        case 'm':
+        {
+            SetDemoPresence(false);
+            break;
+        }
         case 'H':
         {
             SetGameState(gsHalted);
@@ -307,6 +494,14 @@
                 writeCampaignVar(msg.right(msg.size() - 3));
             break;
         }
+        case 'v':
+        {
+            if (msg.at(2) == '?')
+                sendMissionVar(msg.right(msg.size() - 3));
+            else if (msg.at(2) == '!')
+                writeMissionVar(msg.right(msg.size() - 3));
+            break;
+        }
         case 'W':
         {
             // fetch new window resolution via IPC and save it in the settings
@@ -346,6 +541,20 @@
     RawSendIPC(buf);
 }
 
+void HWGame::FromNetWarning(const QString & msg)
+{
+    QByteArray buf;
+    HWProto::addStringToBuffer(buf, "s\x00" + msg + "\x20\x20");
+    RawSendIPC(buf);
+}
+
+void HWGame::FromNetError(const QString & msg)
+{
+    QByteArray buf;
+    HWProto::addStringToBuffer(buf, "s\x05" + msg + "\x20\x20");
+    RawSendIPC(buf);
+}
+
 void HWGame::onClientRead()
 {
     quint8 msglen;
@@ -400,6 +609,10 @@
     arguments << QString::number(resolutions.second.width());
     arguments << "--height";
     arguments << QString::number(resolutions.second.height());
+    if (config->zoom() != 100) {
+        arguments << "--zoom";
+        arguments << QString::number(config->zoom());
+    }
     arguments << "--raw-quality";
     arguments << QString::number(config->translateQuality());
     arguments << "--stereo";
@@ -414,6 +627,8 @@
         arguments << "--nosound";
     if (!config->isMusicEnabled())
         arguments << "--nomusic";
+    if (!config->isAudioDampenEnabled())
+        arguments << "--nodampen";
     if (!nick.isEmpty()) {
         arguments << "--nick";
         arguments << nick;
@@ -427,6 +642,8 @@
         arguments << "--no-healthtag";
     if (config->Form->ui.pageOptions->CBTagOpacity->isChecked())
         arguments << "--translucent-tags";
+    if (!config->isHolidaySillinessEnabled())
+        arguments << "--no-holiday-silliness";
     arguments << "--chat-size";
     arguments << QString::number(config->chatSize());
 
@@ -453,6 +670,19 @@
     SetGameState(gsStarted);
 }
 
+void HWGame::PlayOfficialServerDemo()
+{
+    // TODO: Use gtDemo so fast-forward is available.
+    // Needs engine support first.
+    lastGameStartArgs.clear();
+    lastGameType = gtLocal;
+
+    gameType = gtLocal;
+    demo.clear();
+    Start(false);
+    SetGameState(gsStarted);
+}
+
 void HWGame::StartNet()
 {
     lastGameStartArgs.clear();
@@ -486,16 +716,19 @@
     SetGameState(gsStarted);
 }
 
-void HWGame::StartTraining(const QString & file, const QString & subFolder)
+void HWGame::StartTraining(const QString & file, const QString & subFolder, const QString & trainTeam)
 {
     lastGameStartArgs.clear();
     lastGameStartArgs.append(file);
+    lastGameStartArgs.append(subFolder);
+    lastGameStartArgs.append(trainTeam);
     lastGameType = gtTraining;
-    lastTrainingSubFolder = subFolder;
 
     gameType = gtTraining;
 
-    training = "Missions/" + subFolder + "/" + file + ".lua";
+    trainingScript  = "Missions/" + subFolder + "/" + file + ".lua";
+    trainingName = file;
+    trainingTeam = trainTeam;
     demo.clear();
     Start(false);
     SetGameState(gsStarted);
@@ -524,14 +757,23 @@
     emit GameStateChanged(state);
     if (gameType == gtCampaign)
     {
-      emit CampStateChanged(1);
+        emit CampStateChanged(state);
+    }
+    else if (gameType == gtTraining)
+    {
+        emit TrainingStateChanged(1);
     }
 }
 
+void HWGame::SetDemoPresence(bool hasDemo)
+{
+    emit DemoPresenceChanged(hasDemo);
+}
+
 void HWGame::abort()
 {
     QByteArray buf;
-    HWProto::addStringToBuffer(buf, QString("efinish"));
+    HWProto::addStringToBuffer(buf, QString("eforcequit"));
     RawSendIPC(buf);
 }
 
@@ -560,3 +802,28 @@
     teamfile.setValue("Campaign " + campaign + "/" + varToWrite, varValue);
 }
 
+void HWGame::sendMissionVar(const QByteArray &varToSend)
+{
+    QString varToFind = QString::fromUtf8(varToSend);
+    QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(trainingTeam), QSettings::IniFormat, 0);
+    teamfile.setIniCodec("UTF-8");
+    QString varValue = teamfile.value("Mission " + trainingName + "/" + varToFind, "").toString();
+    QByteArray command;
+    HWProto::addStringToBuffer(command, "v." + varValue);
+    RawSendIPC(command);
+}
+
+void HWGame::writeMissionVar(const QByteArray & varVal)
+{
+    int i = varVal.indexOf(" ");
+    if(i < 0)
+        return;
+
+    QString varToWrite = QString::fromUtf8(varVal.left(i));
+    QString varValue = QString::fromUtf8(varVal.mid(i + 1));
+
+    QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(trainingTeam), QSettings::IniFormat, 0);
+    teamfile.setIniCodec("UTF-8");
+    teamfile.setValue("Mission " + trainingName + "/" + varToWrite, varValue);
+}
+
--- a/QTfrontend/game.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/game.h	Wed Jul 31 23:14:27 2019 +0200
@@ -64,7 +64,6 @@
 // last game info
 extern QList<QVariant> lastGameStartArgs;
 extern GameType lastGameType;
-extern QString lastTrainingSubFolder;
 extern GameCFGWidget * lastGameCfg;
 extern QString lastGameAmmo;
 extern TeamSelWidget * lastGameTeamSel;
@@ -77,10 +76,11 @@
         virtual ~HWGame();
         void AddTeam(const QString & team);
         void PlayDemo(const QString & demofilename, bool isSave);
+        void PlayOfficialServerDemo();
         void StartLocal();
         void StartQuick();
         void StartNet();
-        void StartTraining(const QString & file, const QString & subFolder);
+        void StartTraining(const QString & file, const QString & subFolder, const QString & trainTeam);
         void StartCampaign(const QString & camp, const QString & campScript, const QString & campTeam);
         void abort();
         GameState gameState;
@@ -96,15 +96,19 @@
         void SendChat(const QString & msg);
         void SendTeamMessage(const QString & msg);
         void GameStateChanged(GameState gameState);
+        void DemoPresenceChanged(bool hasDemo);
         void GameStats(char type, const QString & info);
         void HaveRecord(RecordType type, const QByteArray & record);
         void ErrorMessage(const QString &);
         void CampStateChanged(int);
+        void TrainingStateChanged(int);
         void SendConsoleCommand(const QString & command);
 
     public slots:
         void FromNet(const QByteArray & msg);
         void FromNetChat(const QString & msg);
+        void FromNetWarning(const QString & msg);
+        void FromNetError(const QString & msg);
 
     private:
         char msgbuf[MAXMSGCHARS];
@@ -123,8 +127,11 @@
         void SendCampaignConfig();
         void ParseMessage(const QByteArray & msg);
         void SetGameState(GameState state);
+        void SetDemoPresence(bool hasDemo);
         void sendCampaignVar(const QByteArray & varToSend);
         void writeCampaignVar(const QByteArray &varVal);
+        void sendMissionVar(const QByteArray & varToSend);
+        void writeMissionVar(const QByteArray &varVal);
         void flushNetBuffer();
 };
 
--- a/QTfrontend/gameuiconfig.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/gameuiconfig.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -105,15 +105,17 @@
     Form->ui.pageOptions->CBFrontendFullscreen->setChecked(ffscr);
 
     Form->ui.pageOptions->SLQuality->setValue(value("video/quality", 5).toUInt());
+    Form->ui.pageOptions->SLZoom->setValue(value("video/zoom", 100).toUInt());
     Form->ui.pageOptions->CBStereoMode->setCurrentIndex(value("video/stereo", 0).toUInt());
     Form->ui.pageOptions->CBFrontendEffects->setChecked(value("frontend/effects", true).toBool());
     Form->ui.pageOptions->CBSound->setChecked(value("audio/sound", true).toBool());
     Form->ui.pageOptions->CBFrontendSound->setChecked(value("frontend/sound", true).toBool());
     Form->ui.pageOptions->CBMusic->setChecked(value("audio/music", true).toBool());
     Form->ui.pageOptions->CBFrontendMusic->setChecked(value("frontend/music", true).toBool());
+    Form->ui.pageOptions->CBDampenAudio->setChecked(value("audio/dampen", true).toBool());
     Form->ui.pageOptions->SLVolume->setValue(value("audio/volume", 100).toUInt());
 
-    QString netNick = value("net/nick", tr("Guest")+QString("%1").arg(rand())).toString();
+    QString netNick = value("net/nick", getRandomNick()).toString();
     Form->ui.pageOptions->editNetNick->setText(netNick);
     bool savePwd = value("net/savepassword",true).toBool();
     Form->ui.pageOptions->CBSavePassword->setChecked(savePwd);
@@ -241,11 +243,12 @@
 void GameUIConfig::SaveOptions()
 {
     setValue("video/fullscreenResolution", Form->ui.pageOptions->CBResolution->currentText());
-    setValue("video/windowedWidth", Form->ui.pageOptions->windowWidthEdit->text());
-    setValue("video/windowedHeight", Form->ui.pageOptions->windowHeightEdit->text());
+    setValue("video/windowedWidth", Form->ui.pageOptions->windowWidthEdit->value());
+    setValue("video/windowedHeight", Form->ui.pageOptions->windowHeightEdit->value());
     setValue("video/fullscreen", vid_Fullscreen());
 
     setValue("video/quality", Form->ui.pageOptions->SLQuality->value());
+    setValue("video/zoom", Form->ui.pageOptions->SLZoom->value());
     setValue("video/stereo", stereoMode());
 
     setValue("frontend/effects", isFrontendEffects());
@@ -270,6 +273,7 @@
     setValue("audio/music", isMusicEnabled());
     setValue("frontend/music", isFrontendMusicEnabled());
     setValue("audio/volume", Form->ui.pageOptions->SLVolume->value());
+    setValue("audio/dampen", isAudioDampenEnabled());
 
     setValue("net/nick", netNick());
     if (netPasswordIsValid() && Form->ui.pageOptions->CBSavePassword->isChecked()) {
@@ -365,8 +369,8 @@
         full.setWidth(wh[0].toInt());
         full.setHeight(wh[1].toInt());
     }
-    windowed.setWidth(Form->ui.pageOptions->windowWidthEdit->text().toInt());
-    windowed.setHeight(Form->ui.pageOptions->windowHeightEdit->text().toInt());
+    windowed.setWidth(Form->ui.pageOptions->windowWidthEdit->value());
+    windowed.setHeight(Form->ui.pageOptions->windowHeightEdit->value());
     return std::make_pair(full, windowed);
 }
 
@@ -443,6 +447,26 @@
     return Form->ui.pageOptions->CBFrontendFullscreen->isChecked();
 }
 
+quint16 GameUIConfig::zoom()
+{
+    return Form->ui.pageOptions->SLZoom->value();
+}
+
+bool GameUIConfig::isHolidaySillinessEnabled() const
+{
+    return value("misc/holidaySilliness", true).toBool();
+}
+
+int GameUIConfig::quickGameExperience() const
+{
+    return value("misc/quickGameExperience", 0).toInt();
+}
+
+void GameUIConfig::setQuickGameExperience(int exp)
+{
+    setValue("misc/quickGameExperience", exp);
+}
+
 bool GameUIConfig::isSoundEnabled()
 {
     return Form->ui.pageOptions->CBSound->isChecked();
@@ -460,6 +484,10 @@
 {
     return Form->ui.pageOptions->CBFrontendMusic->isChecked();
 }
+bool GameUIConfig::isAudioDampenEnabled()
+{
+    return Form->ui.pageOptions->CBDampenAudio->isChecked();
+}
 
 bool GameUIConfig::isShowFPSEnabled()
 {
@@ -503,6 +531,16 @@
     Form->ui.pageOptions->editNetNick->setText(value("net/nick", "").toString());
 }
 
+QString GameUIConfig::getRandomNick()
+{
+    // Generate random nick name or pick old one if one was already generated.
+    QString nick;
+    if (cachedRandomNick.isNull())
+        // "Guest" + number between 1 and 99999
+        cachedRandomNick = tr("Guest") + QString("%1").arg(rand() % 99999 + 1);
+    return cachedRandomNick;
+}
+
 QByteArray GameUIConfig::netPasswordHash()
 {
     return QCryptographicHash::hash(Form->ui.pageOptions->editNetPassword->text().toUtf8(), QCryptographicHash::Md5).toHex();
--- a/QTfrontend/gameuiconfig.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/gameuiconfig.h	Wed Jul 31 23:14:27 2019 +0200
@@ -47,6 +47,7 @@
         QString language();
         bool isMusicEnabled();
         bool isFrontendMusicEnabled();
+        bool isAudioDampenEnabled();
         bool isShowFPSEnabled();
         bool isAltDamageEnabled();
         bool appendDateTimeToRecordName();
@@ -54,6 +55,7 @@
         quint8 volume();
         quint8 timerInterval();
         QString netNick();
+        QString getRandomNick();
         QByteArray netPasswordHash();
         int netPasswordLength();
         void clearPasswordHash();
@@ -66,6 +68,10 @@
         bool isReducedQuality() const;
         bool isFrontendEffects() const;
         bool isFrontendFullscreen() const;
+        quint16 zoom();
+        bool isHolidaySillinessEnabled() const;
+        int quickGameExperience() const;
+        void setQuickGameExperience(int exp);
         void resizeToConfigValues();
         quint32 stereoMode() const;
         void setValue(const QString & key, const QVariant & value);
@@ -102,6 +108,8 @@
         QList<BindAction> m_binds;
 
         void applyProxySettings();
+
+        QString cachedRandomNick;
 };
 
 #endif
--- a/QTfrontend/hedgewars.qrc	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/hedgewars.qrc	Wed Jul 31 23:14:27 2019 +0200
@@ -1,6 +1,7 @@
 <RCC>
     <qresource prefix="/">
         <file alias="Ammos.png">../share/hedgewars/Data/Graphics/AmmoMenu/Ammos_base.png</file>
+        <file alias="keys.csv">../share/hedgewars/Data/misc/keys.csv</file>
         <file>res/css/qt.css</file>
         <file>res/css/chat.css</file>
         <file>res/css/christmas.css</file>
@@ -35,39 +36,18 @@
         <file>res/botlevels/net3.png</file>
         <file>res/botlevels/net4.png</file>
         <file>res/botlevels/net5.png</file>
-        <file>res/campaign/A_Classic_Fairytale/first_blood.png</file>
-        <file>res/campaign/A_Classic_Fairytale/shadow.png</file>
-        <file>res/campaign/A_Classic_Fairytale/journey.png</file>
-        <file>res/campaign/A_Classic_Fairytale/united.png</file>
-        <file>res/campaign/A_Classic_Fairytale/backstab.png</file>
-        <file>res/campaign/A_Classic_Fairytale/dragon.png</file>
-        <file>res/campaign/A_Classic_Fairytale/family.png</file>
-        <file>res/campaign/A_Classic_Fairytale/queen.png</file>
-        <file>res/campaign/A_Classic_Fairytale/enemy.png</file>
-        <file>res/campaign/A_Classic_Fairytale/epil.png</file>
-        <file>res/campaign/A_Space_Adventure/cosmos.png</file>
-        <file>res/campaign/A_Space_Adventure/moon01.png</file>
-        <file>res/campaign/A_Space_Adventure/moon02.png</file>
-        <file>res/campaign/A_Space_Adventure/ice01.png</file>
-        <file>res/campaign/A_Space_Adventure/ice02.png</file>
-        <file>res/campaign/A_Space_Adventure/desert01.png</file>
-        <file>res/campaign/A_Space_Adventure/desert02.png</file>
-        <file>res/campaign/A_Space_Adventure/desert03.png</file>
-        <file>res/campaign/A_Space_Adventure/fruit01.png</file>
-        <file>res/campaign/A_Space_Adventure/fruit02.png</file>
-        <file>res/campaign/A_Space_Adventure/fruit03.png</file>
-        <file>res/campaign/A_Space_Adventure/death01.png</file>
-        <file>res/campaign/A_Space_Adventure/death02.png</file>
-        <file>res/campaign/A_Space_Adventure/final.png</file>
         <file>res/bonus.png</file>
         <file>res/Hedgehog.png</file>
         <file>res/net.png</file>
         <file>res/About.png</file>
+        <file>res/AboutIcon.png</file>
         <file>res/SimpleGame.png</file>
         <file>res/Campaign.png</file>
         <file>res/CampaignDefault.png</file>
         <file>res/Multiplayer.png</file>
         <file>res/Trainings.png</file>
+        <file>res/Challenges.png</file>
+        <file>res/Scenarios.png</file>
         <file>res/Background.png</file>
         <file>res/BackgroundChristmas.png</file>
         <file>res/BackgroundEaster.png</file>
@@ -81,9 +61,12 @@
         <file>res/audio.png</file>
         <file>res/camera.png</file>
         <file>res/Settings.png</file>
+        <file>res/Help.png</file>
         <file>res/dropdown.png</file>
         <file>res/dropdown_disabled.png</file>
         <file>res/dropdown_selected.png</file>
+        <file>res/keyconflict.png</file>
+        <file>res/keyconflict_selected.png</file>
         <file>res/new.png</file>
         <file>res/edit.png</file>
         <file>res/delete.png</file>
@@ -120,6 +103,10 @@
         <file>res/panelbg.png</file>
         <file>res/lightbulb_on.png</file>
         <file>res/lightbulb_off.png</file>
+        <file>res/scroll_up.png</file>
+        <file>res/scroll_down.png</file>
+        <file>res/scroll_left.png</file>
+        <file>res/scroll_right.png</file>
         <file>res/spin_up.png</file>
         <file>res/spin_up_disabled.png</file>
         <file>res/spin_down.png</file>
@@ -209,6 +196,7 @@
         <file>res/StatsMostSelfDamage.png</file>
         <file>res/StatsSelfKilled.png</file>
         <file>res/StatsSkipped.png</file>
+        <file>res/StatsEverAfter.png</file>
         <file>res/StatsCustomAchievement.png</file>
         <file>res/Start.png</file>
         <file>res/mapRandom.png</file>
@@ -228,10 +216,13 @@
         <file>res/chat/lamp_off.png</file>
         <file>res/chat/ingame.png</file>
         <file>res/splash.png</file>
-        <file>res/html/about.html</file>
+        <file>res/credits.csv</file>
         <file>res/chat/hedgehogcontributor.png</file>
         <file>res/chat/hedgehogcontributor_gray.png</file>
         <file>res/chat/roomadmincontributor.png</file>
         <file>res/chat/roomadmincontributor_gray.png</file>
     </qresource>
+    <qresource lang="en">
+        <file alias="res/Trainings.png">res/Trainings_en.png</file>
+    </qresource>
 </RCC>
--- a/QTfrontend/hwconsts.cpp.in	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/hwconsts.cpp.in	Wed Jul 31 23:14:27 2019 +0200
@@ -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 <QStandardItemModel>
 
 #include "hwconsts.h"
@@ -28,6 +34,8 @@
 QString * cRevisionString = new QString("${HEDGEWARS_REVISION}");
 QString * cHashString = new QString("${HEDGEWARS_HASH}");
 
+// For disallowing some characters that would screw up file name
+QString * cSafeFileNameRegExp = new QString("[^:/\\\\]*");
 
 QDir * bindir = new QDir();
 QDir * cfgdir = new QDir();
@@ -37,7 +45,9 @@
 bool custom_data = false;
 
 int cMaxTeams = 8;
+int cMaxHHs = HEDGEHOGS_PER_TEAM * cMaxTeams;
 int cMinServerVersion = 3;
+unsigned char cInvertTextColorAt = 64;
 
 QString * cDefaultAmmoStore = new QString( AMMOLINE_DEFAULT_QT AMMOLINE_DEFAULT_PROB
                                            AMMOLINE_DEFAULT_DELAY AMMOLINE_DEFAULT_CRATE );
@@ -72,6 +82,9 @@
         << qMakePair(QString("Highlander"), QString(
             AMMOLINE_HIGHLANDER_QT AMMOLINE_HIGHLANDER_PROB
             AMMOLINE_HIGHLANDER_DELAY AMMOLINE_HIGHLANDER_CRATE ))
+        << qMakePair(QString("Balanced Random Weapon"), QString(
+            AMMOLINE_BRW_QT AMMOLINE_BRW_PROB
+            AMMOLINE_BRW_DELAY AMMOLINE_BRW_CRATE ))
         << qMakePair(QString("Construction Mode"),   QString(
             AMMOLINE_CONSTRUCTION_QT AMMOLINE_CONSTRUCTION_PROB
             AMMOLINE_CONSTRUCTION_DELAY AMMOLINE_CONSTRUCTION_CRATE ))
@@ -83,6 +96,33 @@
             AMMOLINE_HEDGEEDITOR_DELAY AMMOLINE_HEDGEEDITOR_CRATE ))
         ;
 
+QStringList cQuickGameMaps = QStringList()
+    << "Bamboo"
+    << "Bath"
+    << "Battlefield"
+    << "Blox"
+    << "Bubbleflow"
+    << "Cake"
+    << "Castle"
+    << "Cheese"
+    << "Cogs"
+    << "CrazyMission"
+    << "EarthRise"
+    << "Eyes"
+    << "Hammock"
+    << "HedgeFortress"
+    << "Hedgelove"
+    << "Hedgewars"
+    << "Hydrant"
+    << "Lonely_Island"
+    << "Mushrooms"
+    << "Octorama"
+    << "PirateFlag"
+    << "Plane"
+    << "Sheep"
+    << "Trash"
+    << "Tree";
+
 unsigned int colors[] = HW_TEAMCOLOR_ARRAY;
 
 QString * netHost = new QString();
--- a/QTfrontend/hwconsts.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/hwconsts.h	Wed Jul 31 23:14:27 2019 +0200
@@ -28,6 +28,7 @@
 extern QString * cRevisionString;
 extern QString * cHashString;
 extern QString * cDataDir;
+extern QString * cSafeFileNameRegExp;
 
 extern QDir * bindir;
 extern QDir * cfgdir;
@@ -37,7 +38,9 @@
 extern bool custom_data;
 
 extern int cMaxTeams;
+extern int cMaxHHs;
 extern int cMinServerVersion;
+extern unsigned char cInvertTextColorAt;
 
 class QStandardItemModel;
 
@@ -45,6 +48,7 @@
 extern QString * cEmptyAmmoStore;
 extern int cAmmoNumber;
 extern QList< QPair<QString, QString> > cDefaultAmmos;
+extern QStringList cQuickGameMaps;
 
 extern unsigned int colors[];
 
@@ -73,6 +77,12 @@
 #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
+
+// Default clan colors
+// NOTE: Always keep this in sync with hedgewars/uVariables.pas (ClanColorArray)
 
 // see https://en.wikipedia.org/wiki/List_of_colors
 /*define HW_TEAMCOLOR_ARRAY  {0xff007fff, /. azure          ./ \
@@ -105,7 +115,7 @@
                               0xffe55bb0, /* pink   */ \
                               0xff20bf00, /* green  */ \
                               0xfffe8b0e, /* orange */ \
-                              0xff5f3605, /* brown  */ \
+                              0xff8f5902, /* brown  */ \
                               0xffffff01, /* yellow */ \
                               /* add new colors here */ \
                               0 }
--- a/QTfrontend/hwform.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/hwform.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -57,6 +57,7 @@
 #include "hwform.h"
 #include "game.h"
 #include "team.h"
+#include "mission.h"
 #include "campaign.h"
 #include "teamselect.h"
 #include "selectWeapon.h"
@@ -128,6 +129,7 @@
 // I started handing this down to each place it touches, but it was getting ridiculous
 // and this one flag does not warrant a static class
 bool frontendEffects = true;
+bool demoIsPresent = true;
 QString playerHash;
 
 QIcon finishedIcon;
@@ -167,7 +169,10 @@
 
     config = new GameUIConfig(this, DataManager::instance().settingsFileName());
     frontendEffects = config->value("frontend/effects", true).toBool();
-    playerHash = QString(QCryptographicHash::hash(config->value("net/nick",tr("Guest")+QString("%1").arg(rand())).toString().toUtf8(), QCryptographicHash::Md5).toHex());
+    bool frontendSounds = config->value("frontend/sound", true).toBool();
+    onFrontendSoundsToggled(frontendSounds);
+
+    playerHash = QString(QCryptographicHash::hash(config->value("net/nick", config->getRandomNick()).toString().toUtf8(), QCryptographicHash::Md5).toHex());
 
     // Icons for finished missions
     finishedIcon.addFile(":/res/missionFinished.png", QSize(), QIcon::Normal, QIcon::On);
@@ -218,6 +223,7 @@
     previousTeamName = "";
     UpdateTeamsLists();
     InitCampaignPage();
+    RestoreSingleplayerTeamSelection();
     UpdateCampaignPage(0);
     UpdateCampaignPageTeam(0);
     UpdateCampaignPageMission(0);
@@ -240,12 +246,16 @@
 
     connect(ui.pageMain->BtnFeedback, SIGNAL(clicked()), this, SLOT(showFeedbackDialog()));
 
+    connect(ui.pageMain->BtnTitle, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
+    pageSwitchMapper->setMapping(ui.pageMain->BtnTitle, ID_PAGE_INFO);
+
     connect(ui.pageMain->BtnInfo, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageMain->BtnInfo, ID_PAGE_INFO);
 
     connect(ui.pageMain->BtnDataDownload, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageMain->BtnDataDownload, ID_PAGE_DATADOWNLOAD);
 
+    connect(ui.pageMain->BtnHelp, SIGNAL(clicked()), this, SLOT(GoToHelp()));
 
 #ifdef VIDEOREC
     connect(ui.pageMain->BtnVideos, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
@@ -283,12 +293,13 @@
     connect(ui.pageOptions->SchemeNew, SIGNAL(clicked()), this, SLOT(GoToNewScheme()));
     connect(ui.pageOptions->SchemeDelete, SIGNAL(clicked()), this, SLOT(DeleteScheme()));
     connect(ui.pageOptions->CBFrontendEffects, SIGNAL(toggled(bool)), this, SLOT(onFrontendEffects(bool)) );
+    connect(ui.pageOptions->CBFrontendSound, SIGNAL(toggled(bool)), this, SLOT(onFrontendSoundsToggled(bool)));
 
     connect(ui.pageNet->BtnSpecifyServer, SIGNAL(clicked()), this, SLOT(NetConnect()));
     connect(ui.pageNet->BtnNetSvrStart, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageNet->BtnNetSvrStart, ID_PAGE_NETSERVER);
 
-    connect(ui.pageNet, SIGNAL(connectClicked(const QString &, quint16)), this, SLOT(NetConnectServer(const QString &, quint16)));
+    connect(ui.pageNet, SIGNAL(connectClicked(const QString &, quint16, bool)), this, SLOT(NetConnectServer(const QString &, quint16, bool)));
 
     connect(ui.pageNetServer->BtnStart, SIGNAL(clicked()), this, SLOT(NetStartServer()));
 
@@ -329,6 +340,9 @@
     connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPageTeam(int)));
     connect(ui.pageCampaign->CBCampaign, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPage(int)));
     connect(ui.pageCampaign->CBMission, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPageMission(int)));
+    connect(ui.pageTraining->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateTrainingPageTeam(int)));
+    connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), ui.pageTraining->CBTeam, SLOT(setCurrentIndex(int)));
+    connect(ui.pageTraining->CBTeam, SIGNAL(currentIndexChanged(int)), ui.pageCampaign->CBTeam, SLOT(setCurrentIndex(int)));
 
     connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsDeleted(QString)),
              this, SLOT(DeleteWeapons(QString)));
@@ -401,6 +415,11 @@
         wBackground->stopAnimation();
 }
 
+void HWForm::onFrontendSoundsToggled(bool value)
+{
+    ui.pageEditTeam->frontendSoundsToggled(value);
+}
+
 /*
 void HWForm::keyReleaseEvent(QKeyEvent *event)
 {
@@ -515,7 +534,7 @@
 
     if(teamslist.empty())
     {
-        QString currentNickName = config->value("net/nick",tr("Guest")+QString("%1").arg(rand())).toString();
+        QString currentNickName = config->value("net/nick", config->getRandomNick()).toString();
         QString teamName;
         int firstHumanTeam = 1;
         int lastHumanTeam = 2;
@@ -538,6 +557,7 @@
         // TODO: Remove DLC filtering when it isn't neccessary anymore
         HWNamegen::teamRandomGrave(defaultTeam, false);
         HWNamegen::teamRandomFort(defaultTeam, false);
+        HWNamegen::teamLocalizedDefaultVoice(defaultTeam);
         defaultTeam.saveToFile();
         teamslist.push_back(teamName);
 
@@ -551,6 +571,7 @@
             HWTeam numberTeam(teamName);
             HWNamegen::teamRandomGrave(numberTeam, false);
             HWNamegen::teamRandomFort(numberTeam, false);
+            HWNamegen::teamLocalizedDefaultVoice(numberTeam);
             numberTeam.saveToFile();
             teamslist.push_back(teamName);
         }
@@ -562,6 +583,7 @@
             HWTeam numberTeam(teamName);
             HWNamegen::teamRandomGrave(numberTeam, false);
             HWNamegen::teamRandomFort(numberTeam, false);
+            HWNamegen::teamLocalizedDefaultVoice(numberTeam);
             numberTeam.setDifficulty(6-i);
             numberTeam.saveToFile();
             teamslist.push_back(teamName);
@@ -571,7 +593,9 @@
     ui.pageOptions->CBTeamName->clear();
     ui.pageOptions->CBTeamName->addItems(teamslist);
     ui.pageCampaign->CBTeam->clear();
-    /* Only show human teams in campaign page */
+    ui.pageTraining->CBTeam->clear();
+    /* Only show human teams in campaign/training page */
+    bool playable = false;
     for(int i=0; i<teamslist.length(); i++)
     {
         HWTeam testTeam = HWTeam(teamslist[i]);
@@ -579,8 +603,15 @@
         if(testTeam.difficulty() == 0)
         {
             ui.pageCampaign->CBTeam->addItem(teamslist[i]);
+            ui.pageTraining->CBTeam->addItem(teamslist[i]);
+            playable = true;
         }
     }
+    ui.pageCampaign->BtnStartCampaign->setEnabled(playable);
+    ui.pageCampaign->btnPreview->setEnabled(playable);
+    ui.pageTraining->btnStart->setEnabled(playable);
+    ui.pageTraining->btnPreview->setEnabled(playable);
+    UpdateTrainingPageTeam(0);
 }
 
 void HWForm::GoToNewWeapons()
@@ -641,6 +672,13 @@
     GoToPage(ID_PAGE_SCHEME);
 }
 
+void HWForm::GoToHelp()
+{
+    // For now just opens the Hedgewars Wiki in external browser.
+    // TODO: Replace this with an offline help someday (bug 660).
+    QDesktopServices::openUrl(QUrl("https://hedgewars.org/wiki"));
+}
+
 void HWForm::GoToVideos()
 {
     GoToPage(ID_PAGE_VIDEOS);
@@ -778,12 +816,15 @@
         case gtQLocal:
         case gtTraining:
         case gtCampaign:
+        case gtDemo:
+        case gtSave:
             ui.pageGameStats->restartBtnVisible(true);
             break;
         default:
             ui.pageGameStats->restartBtnVisible(false);
             break;
         }
+        ui.pageGameStats->saveDemoBtnEnabled(demoIsPresent);
     }
 
     if (id == ID_PAGE_MAIN)
@@ -913,6 +954,11 @@
             GoBack();
         }
 
+    if (curid == ID_PAGE_CAMPAIGN)
+        config->setValue("frontend/lastSingleplayerTeam", ui.pageCampaign->CBTeam->currentText());
+    if (curid == ID_PAGE_TRAINING)
+        config->setValue("frontend/lastSingleplayerTeam", ui.pageTraining->CBTeam->currentText());
+
     if (curid == ID_PAGE_ROOMSLIST || curid == ID_PAGE_CONNECTING) NetDisconnect();
     if (curid == ID_PAGE_NETGAME && hwnet && hwnet->isInRoom()) hwnet->partRoom();
     // need to work on this, can cause invalid state for admin quit trying to prevent bad state message on kick
@@ -1125,18 +1171,18 @@
 void HWForm::NetConnectQuick(const QString & host, quint16 port)
 {
     GoToPage(ID_PAGE_MAIN);
-    NetConnectServer(host, port);
+    NetConnectServer(host, port, false);
 }
 
-void HWForm::NetConnectServer(const QString & host, quint16 port)
+void HWForm::NetConnectServer(const QString & host, quint16 port, bool useTls)
 {
     qDebug("connecting to %s:%d", qPrintable(host), port);
-    _NetConnect(host, port, ui.pageOptions->editNetNick->text().trimmed());
+    _NetConnect(host, port, useTls, ui.pageOptions->editNetNick->text().trimmed());
 }
 
 void HWForm::NetConnectOfficialServer()
 {
-    NetConnectServer(NETGAME_DEFAULT_SERVER, NETGAME_DEFAULT_PORT);
+    NetConnectServer(NETGAME_DEFAULT_SERVER, NETGAME_DEFAULT_PORT, false);
 }
 
 void HWForm::NetPassword(const QString & nick)
@@ -1231,7 +1277,7 @@
         if (retry && hwnet) {
             if (hwnet->m_private_game) {
                 QStringList list = hwnet->getHost().split(":");
-                NetConnectServer(list.at(0), list.at(1).toShort());
+                NetConnectServer(list.at(0), list.at(1).toShort(), false);
             } else
                 NetConnectOfficialServer();
         }
@@ -1320,7 +1366,7 @@
         ui.pageRoomsList->displayWarning(wrnmsg);
 }
 
-void HWForm::_NetConnect(const QString & hostName, quint16 port, QString nick)
+void HWForm::_NetConnect(const QString & hostName, quint16 port, bool useTls, QString nick)
 {
     Q_UNUSED(nick);
 
@@ -1336,6 +1382,8 @@
     GoToPage(ID_PAGE_CONNECTING);
 
     connect(hwnet, SIGNAL(AskForRunGame()), this, SLOT(CreateNetGame()), Qt::QueuedConnection);
+    connect(hwnet, SIGNAL(AskForOfficialServerDemo()), this, SLOT(PlayOfficialServerDemo()), Qt::QueuedConnection);
+    connect(hwnet, SIGNAL(redirected(quint16)), this, SLOT(NetRedirected(quint16)), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(connected()), this, SLOT(NetConnected()), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(Error(const QString&)), this, SLOT(NetError(const QString&)), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(Warning(const QString&)), this, SLOT(NetWarning(const QString&)), Qt::QueuedConnection);
@@ -1496,11 +1544,11 @@
     if (hwnet->m_private_game == false && AskForNickAndPwd() != 0)
         return;
 
-    QString nickname = config->value("net/nick",tr("Guest")+QString("%1").arg(rand())).toString();
+    QString nickname = config->value("net/nick", config->getRandomNick()).toString();
     ui.pageRoomsList->setUser(nickname);
     ui.pageNetGame->setUser(nickname);
 
-    hwnet->Connect(hostName, port, nickname);
+    hwnet->Connect(hostName, port, useTls, nickname);
 }
 
 int HWForm::AskForNickAndPwd(void)
@@ -1515,7 +1563,7 @@
     QString password;
 
     do {
-        nickname = config->value("net/nick",tr("Guest")+QString("%1").arg(rand())).toString();
+        nickname = config->value("net/nick", config->getRandomNick()).toString();
         hash = config->passwordHash();
         temphash = config->tempHash();
 
@@ -1554,7 +1602,7 @@
                 if (retry) {
                     if (hwnet->m_private_game) {
                         QStringList list = hwnet->getHost().split(":");
-                        NetConnectServer(list.at(0), list.at(1).toShort());
+                        NetConnectServer(list.at(0), list.at(1).toShort(), false);
                     } else
                         NetConnectOfficialServer();
                 }
@@ -1600,7 +1648,7 @@
         delete netHost;
         netHost = new QString(hpd->leHost->text());
         netPort = hpd->sbPort->value();
-        NetConnectServer(*netHost, netPort);
+        NetConnectServer(*netHost, netPort, false);
     }
     delete hpd;
 }
@@ -1628,7 +1676,7 @@
 
 void HWForm::AsyncNetServerStart()
 {
-    NetConnectServer("localhost", pnetserver->getRunningPort());
+    NetConnectServer("localhost", pnetserver->getRunningPort(), false);
 }
 
 void HWForm::NetDisconnect()
@@ -1657,7 +1705,7 @@
         if (retry) {
             if (hwnet->m_private_game) {
                 QStringList list = hwnet->getHost().split(":");
-                NetConnectServer(list.at(0), list.at(1).toUInt());
+                NetConnectServer(list.at(0), list.at(1).toUInt(), false);
             } else
                 NetConnectOfficialServer();
         }
@@ -1672,8 +1720,9 @@
     }
     if (pnetserver)
         return; // we have server - let it care of all things
-    if (hwnet) {
-        QString errorStr = QMessageBox::tr("Connection to server is lost") + (reason.isEmpty()?"":("\n\n" + HWNewNet::tr("Quit reason: ") + '"' + reason +'"'));
+    if (hwnet && (reason != "bye"))
+    {
+        QString errorStr = QMessageBox::tr("The connection to the server is lost.") + (reason.isEmpty()?"":("\n\n" + HWNewNet::tr("Reason:") + "\n" + reason));
         MessageDialog::ShowErrorMessage(errorStr, this);
     }
 
@@ -1684,6 +1733,28 @@
     }
 }
 
+void HWForm::NetRedirected(quint16 port)
+{
+    QMessageBox questionMsg(this);
+    questionMsg.setIcon(QMessageBox::Question);
+    questionMsg.setWindowTitle(QMessageBox::tr("Server redirection"));
+    questionMsg.setText(QMessageBox::tr("This server supports secure connections on port %1.\nWould you like to reconnect securely?").arg(port));
+    questionMsg.setTextFormat(Qt::PlainText);
+    questionMsg.setWindowModality(Qt::WindowModal);
+    questionMsg.addButton(QMessageBox::Yes);
+    questionMsg.addButton(QMessageBox::No);
+
+    if (questionMsg.exec() == QMessageBox::Yes)
+    {        
+        QString host = hwnet->getHost().split(":").at(0);
+        NetConnectServer(host, port, true);
+    }
+    else if (hwnet)
+    {
+        hwnet->ContinueConnection();
+    }
+}
+
 void HWForm::NetConnected()
 {
     GoToPage(ID_PAGE_ROOMSLIST);
@@ -1707,14 +1778,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()
@@ -1732,6 +1795,7 @@
     {
         case gsStarted:
         {
+            demoIsPresent = true;
             Music(false);
             if (wBackground) wBackground->stopAnimation();
             if (!hwnet || (!hwnet->isRoomChief() || !hwnet->isInRoom())) GoToPage(ID_PAGE_INGAME);
@@ -1753,7 +1817,15 @@
             Music(ui.pageOptions->CBFrontendMusic->isChecked());
             if (wBackground) wBackground->startAnimation();
             GoToPage(ID_PAGE_GAMESTATS);
-            if (hwnet && (!game || !game->netSuspend)) hwnet->gameFinished(true);
+            if (hwnet)
+            {
+                if (!game || !game->netSuspend)
+                    hwnet->gameFinished(true);
+                // After a game, the local player might have pseudo-teams left
+                // when rejoining a previously left game. This makes sure the
+                // teams list is in a consistent state.
+                ui.pageNetGame->cleanupFakeNetTeams();
+            }
             if (game) game->netSuspend = false;
             break;
         }
@@ -1777,11 +1849,18 @@
     }
 }
 
+void HWForm::DemoPresenceChanged(bool hasDemo)
+{
+    demoIsPresent = hasDemo;
+}
+
 void HWForm::CreateGame(GameCFGWidget * gamecfg, TeamSelWidget* pTeamSelWidget, QString ammo)
 {
     game = new HWGame(config, gamecfg, ammo, pTeamSelWidget);
     connect(game, SIGNAL(CampStateChanged(int)), this, SLOT(UpdateCampaignPageProgress(int)));
+    connect(game, SIGNAL(TrainingStateChanged(int)), this, SLOT(UpdateTrainingPageTeam(int)));
     connect(game, SIGNAL(GameStateChanged(GameState)), this, SLOT(GameStateChanged(GameState)));
+    connect(game, SIGNAL(DemoPresenceChanged(bool)), this, SLOT(DemoPresenceChanged(bool)));
     connect(game, SIGNAL(GameStats(char, const QString &)), ui.pageGameStats, SLOT(GameStats(char, const QString &)));
     connect(game, SIGNAL(ErrorMessage(const QString &)), this, SLOT(ShowFatalErrorMessage(const QString &)), Qt::QueuedConnection);
     connect(game, SIGNAL(HaveRecord(RecordType, const QByteArray &)), this, SLOT(GetRecord(RecordType, const QByteArray &)));
@@ -1833,7 +1912,8 @@
 {
     CreateGame(0, 0, 0);
 
-    game->StartTraining(scriptName, subFolder);
+    QString trainTeam = ui.pageTraining->CBTeam->currentText();
+    game->StartTraining(scriptName, subFolder, trainTeam);
 }
 
 void HWForm::StartCampaign()
@@ -1864,10 +1944,29 @@
     connect(game, SIGNAL(SendConsoleCommand(const QString&)), hwnet, SLOT(consoleCommand(const QString&)));
     connect(game, SIGNAL(SendTeamMessage(const QString &)), hwnet, SLOT(SendTeamMessage(const QString &)));
     connect(hwnet, SIGNAL(chatStringFromNet(const QString &)), game, SLOT(FromNetChat(const QString &)), Qt::QueuedConnection);
+    connect(hwnet, SIGNAL(Warning(const QString&)), game, SLOT(FromNetWarning(const QString&)), Qt::QueuedConnection);
+    connect(hwnet, SIGNAL(Error(const QString&)), game, SLOT(FromNetError(const QString&)), Qt::QueuedConnection);
 
     game->StartNet();
 }
 
+void HWForm::PlayOfficialServerDemo()
+{
+    // go back in pages to prevent user from being stuck on certain pages
+    if(ui.Pages->currentIndex() == ID_PAGE_GAMESTATS ||
+       ui.Pages->currentIndex() == ID_PAGE_INGAME)
+        GoBack();
+
+    QString ammo;
+    ammo = ui.pageNetGame->pGameCFG->WeaponsName->itemData(
+               ui.pageNetGame->pGameCFG->WeaponsName->currentIndex()
+           ).toString();
+
+    CreateGame(ui.pageNetGame->pGameCFG, ui.pageNetGame->pNetTeamsWidget, ammo);
+
+    game->PlayOfficialServerDemo();
+}
+
 void HWForm::closeEvent(QCloseEvent *event)
 {
     config->SaveOptions();
@@ -1902,6 +2001,7 @@
     ui.pageNetGame->pGameCFG->GameSchemes->setModel(gameSchemeModel);
     ui.pageNetGame->pGameCFG->setMaster(true);
     ui.pageNetGame->pNetTeamsWidget->setInteractivity(true);
+    ui.pageNetGame->pGameCFG->resetSchemeStates();
 
     if (hwnet)
     {
@@ -1986,6 +2086,36 @@
     }
 }
 
+void HWForm::UpdateTrainingPageTeam(int index)
+{
+    Q_UNUSED(index);
+    HWTeam team(ui.pageTraining->CBTeam->currentText());
+    QString tName = team.name();
+
+    QListWidget* listWidget;
+    for(int w = 0; w < 3; w++)
+    {
+        switch(w) {
+            case 0: listWidget = ui.pageTraining->lstTrainings; break;
+            case 1: listWidget = ui.pageTraining->lstChallenges; break;
+            case 2: listWidget = ui.pageTraining->lstScenarios; break;
+            default: listWidget = ui.pageTraining->lstTrainings; break;
+        }
+        unsigned int n = listWidget->count();
+
+        for(unsigned int i = 0; i < n; i++)
+        {
+            QListWidgetItem* item = listWidget->item(i);
+            QString missionName = QString(item->data(Qt::UserRole).toString()).replace(QString(" "),QString("_"));
+            if(isMissionWon(missionName, tName))
+                item->setIcon(finishedIcon);
+            else
+                item->setIcon(notFinishedIcon);
+        }
+    }
+    ui.pageTraining->updateInfo();
+}
+
 void HWForm::InitCampaignPage()
 {
     ui.pageCampaign->CBCampaign->clear();
@@ -2005,26 +2135,62 @@
         QString tName = team.name();
         ui.pageCampaign->CBCampaign->addItem(getRealCampName(campaignName), campaignName);
     }
+
+}
+
+void HWForm::RestoreSingleplayerTeamSelection()
+{
+    QString lastTeam = config->value("frontend/lastSingleplayerTeam", QString()).toString();
+    if (!lastTeam.isNull() && !lastTeam.isEmpty())
+    {
+        int index = ui.pageCampaign->CBTeam->findData(lastTeam, Qt::DisplayRole);
+        if(index != -1)
+        {
+            ui.pageCampaign->CBTeam->setCurrentIndex(index);
+            UpdateCampaignPageTeam(index);
+        }
+        index = ui.pageTraining->CBTeam->findData(lastTeam, Qt::DisplayRole);
+        if(index != -1)
+        {
+            ui.pageTraining->CBTeam->setCurrentIndex(index);
+            UpdateTrainingPageTeam(index);
+        }
+    }
 }
 
 void HWForm::UpdateCampaignPage(int index)
 {
     Q_UNUSED(index);
     HWTeam team(ui.pageCampaign->CBTeam->currentText());
-    QString campaignName = ui.pageCampaign->CBCampaign->itemData(ui.pageCampaign->CBCampaign->currentIndex()).toString();
+    QString campaignName = ui.pageCampaign->CBCampaign->currentData().toString();
     QString tName = team.name();
 
     campaignMissionInfo = getCampMissionList(campaignName,tName);
     ui.pageCampaign->CBMission->clear();
 
+    // Populate mission list
     for(int i=0;i<campaignMissionInfo.size();i++)
     {
         ui.pageCampaign->CBMission->addItem(QString(campaignMissionInfo[i].realName), QString(campaignMissionInfo[i].name));
-        if(isMissionWon(campaignName, i, tName))
+        if(isCampMissionWon(campaignName, i, tName))
             ui.pageCampaign->CBMission->setItemIcon(i, finishedIcon);
         else
             ui.pageCampaign->CBMission->setItemIcon(i, notFinishedIcon);
     }
+
+    // Select first open mission
+    int missionIndex = ui.pageCampaign->CBMission->currentIndex();
+    if(isCampMissionWon(campaignName, missionIndex, tName))
+    {
+        for(int m = 0; m < ui.pageCampaign->CBMission->count(); m++)
+        {
+            if(!isCampMissionWon(campaignName, m, tName))
+            {
+                ui.pageCampaign->CBMission->setCurrentIndex(m);
+                break;
+            }
+        }
+    }
 }
 
 void HWForm::UpdateCampaignPageTeam(int index)
@@ -2041,6 +2207,7 @@
 
     unsigned int n = entries.count();
 
+    // Update campaign status
     for(unsigned int i = 0; i < n; i++)
     {
         QString campaignName = QString(entries[i]).replace(QString(" "),QString("_"));
@@ -2054,7 +2221,7 @@
 void HWForm::UpdateCampaignPageMission(int index)
 {
     // update thumbnail and description
-    QString campaignName = ui.pageCampaign->CBCampaign->itemData(ui.pageCampaign->CBCampaign->currentIndex()).toString();
+    QString campaignName = ui.pageCampaign->CBCampaign->currentData().toString();
     // when campaign changes the UpdateCampaignPageMission is triggered with wrong values
     // this will cause segfault. This check prevents illegal memory reads
     if(index > -1 && index < campaignMissionInfo.count()) {
@@ -2066,26 +2233,74 @@
 
 void HWForm::UpdateCampaignPageProgress(int index)
 {
-    Q_UNUSED(index);
-
-    QString missionTitle = ui.pageCampaign->CBMission->currentText();
+    QString missionTitle = ui.pageCampaign->CBMission->currentData().toString();
     UpdateCampaignPage(0);
+    int missionIndex = 0;
+    // Restore selected mission (because UpdateCampaignPage repopulated the list)
     for(int i=0;i<ui.pageCampaign->CBMission->count();i++)
     {
-        if (ui.pageCampaign->CBMission->itemData(i)==missionTitle)
+        if (ui.pageCampaign->CBMission->itemData(i).toString() == missionTitle)
         {
-            ui.pageCampaign->CBMission->setCurrentIndex(i);
+            missionIndex = i;
             break;
         }
     }
-    int i = ui.pageCampaign->CBCampaign->currentIndex();
-    QString campaignName = ui.pageCampaign->CBCampaign->itemData(i).toString();
+
+    // Get metadata
+    int c = ui.pageCampaign->CBCampaign->currentIndex();
+    QString campaignName = ui.pageCampaign->CBCampaign->itemData(c).toString();
     HWTeam team(ui.pageCampaign->CBTeam->currentText());
     QString tName = team.name();
+
+    if(index == gsFinished)
+    {
+        // Select new mission when current mission went from
+        // unfinished to finished.
+        if(ui.pageCampaign->currentMissionWon == false &&
+           isCampMissionWon(campaignName, missionIndex, tName))
+        {
+            // Traverse all missions and pick first mission that
+            // has not been won.
+            bool selected = false;
+            // start from mission that comes after the selected one
+            for(int m = missionIndex-1; m >= 0;m--)
+            {
+                if(!isCampMissionWon(campaignName, m, tName))
+                {
+                    missionIndex = m;
+                    selected = true;
+                    break;
+                }
+            }
+            // No mission selected? Let's try again from the end of the list
+            if(!selected)
+            {
+                for(int m = ui.pageCampaign->CBMission->count()-1; m > missionIndex-1; m--)
+                {
+                    if(!isCampMissionWon(campaignName, m, tName))
+                    {
+                        missionIndex = m;
+                        break;
+                    }
+                }
+            }
+            // If no mission was selected, the old selection remains unchanged.
+        }
+    }
+    else if(index == gsStarted)
+    {
+        // Remember the "won" state of current mission before we start it.
+        // We'll need it when the game has finished.
+        ui.pageCampaign->currentMissionWon = isCampMissionWon(campaignName, missionIndex, tName);
+    }
+
+    ui.pageCampaign->CBMission->setCurrentIndex(missionIndex);
+
+    // Update campaign victory status
     if(isCampWon(campaignName, tName))
-        ui.pageCampaign->CBCampaign->setItemIcon(i, finishedIcon);
+        ui.pageCampaign->CBCampaign->setItemIcon(c, finishedIcon);
     else
-        ui.pageCampaign->CBCampaign->setItemIcon(i, notFinishedIcon);
+        ui.pageCampaign->CBCampaign->setItemIcon(c, notFinishedIcon);
 }
 
 // used for --set-everything [screen width] [screen height] [color dept] [volume] [enable music] [enable sounds] [language file] [full screen] [show FPS] [alternate damage] [timer value] [reduced quality]
@@ -2106,9 +2321,11 @@
                    + " --fullscreen-height " + QString::number(resolutions.first.height())
                    + " --width " + QString::number(resolutions.second.width())
                    + " --height " + QString::number(resolutions.second.height())
+                   + (config->zoom() == 100 ? "" : " --zoom " + QString::number(config->zoom()))
                    + " --volume " + QString::number(config->volume())
                    + (config->isMusicEnabled() ? "" : " --nomusic")
                    + (config->isSoundEnabled() ? "" : " --nosound")
+                   + (config->isAudioDampenEnabled() ? "" : " --nodampen")
                    + " --locale " + config->language() + ".txt"
                    + (config->vid_Fullscreen() ? " --fullscreen" : "")
                    + (config->isShowFPSEnabled() ? " --showfps" : "")
@@ -2118,7 +2335,8 @@
                    + (!config->Form->ui.pageOptions->CBTeamTag->isChecked() ? " --no-teamtag" : "")
                    + (!config->Form->ui.pageOptions->CBHogTag->isChecked() ? " --no-hogtag" : "")
                    + (!config->Form->ui.pageOptions->CBHealthTag->isChecked() ? " --no-healthtag" : "")
-                   + (config->Form->ui.pageOptions->CBTagOpacity->isChecked() ? " --translucent-tags" : "");
+                   + (config->Form->ui.pageOptions->CBTagOpacity->isChecked() ? " --translucent-tags" : "")
+                   + (!config->isHolidaySillinessEnabled() ? " --no-holiday-silliness" : "");
 }
 
 void HWForm::AssociateFiles()
@@ -2173,6 +2391,7 @@
         infoMsg.setIcon(QMessageBox::Information);
         infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
         infoMsg.setText(QMessageBox::tr("All file associations have been set"));
+        infoMsg.setTextFormat(Qt::PlainText);
         infoMsg.setWindowModality(Qt::WindowModal);
         infoMsg.exec();
     }
@@ -2223,7 +2442,7 @@
 
     switch(lastGameType) {
     case gtTraining:
-        game->StartTraining(lastGameStartArgs.at(0).toString(), lastTrainingSubFolder);
+        game->StartTraining(lastGameStartArgs.at(0).toString(), lastGameStartArgs.at(1).toString(), lastGameStartArgs.at(2).toString());
         break;
     case gtQLocal:
         game->StartQuick();
@@ -2234,6 +2453,10 @@
     case gtLocal:
         game->StartLocal();
         break;
+    case gtDemo:
+    case gtSave:
+        PlayDemo();
+        break;
     default:
         break;
     }
@@ -2286,6 +2509,7 @@
     questionMsg.setIcon(QMessageBox::Question);
     questionMsg.setWindowTitle(QMessageBox::tr("Not all players are ready"));
     questionMsg.setText(QMessageBox::tr("Are you sure you want to start this game?\nNot all players are ready."));
+    questionMsg.setTextFormat(Qt::PlainText);
     questionMsg.setWindowModality(Qt::WindowModal);
     questionMsg.addButton(QMessageBox::Yes);
     questionMsg.addButton(QMessageBox::Cancel);
--- a/QTfrontend/hwform.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/hwform.h	Wed Jul 31 23:14:27 2019 +0200
@@ -52,6 +52,7 @@
 class QSignalMapper;
 
 extern bool frontendEffects;
+extern bool demoIsPresent;
 extern QString playerHash;
 
 class HWForm : public QMainWindow
@@ -75,6 +76,7 @@
         void GoToSaves();
         void GoToDemos();
         void GoToNet();
+        void GoToHelp();
         void GoToEditWeapons();
         void GoToNewWeapons();
         void GoToWeapons(int index);
@@ -99,9 +101,10 @@
         void startTraining(const QString&, const QString&);
         void StartCampaign();
         void NetConnect();
-        void NetConnectServer(const QString & host, quint16 port);
+        void NetConnectServer(const QString & host, quint16 port, bool useTls);
         void NetConnectOfficialServer();
         void NetStartServer();
+        void NetRedirected(quint16 port);
         void NetDisconnect();
         void NetConnected();
         void NetError(const QString & errmsg);
@@ -119,22 +122,27 @@
         void RemoveNetTeam(const HWTeam& team);
         void StartMPGame();
         void GameStateChanged(GameState gameState);
+        void DemoPresenceChanged(bool hasDemo);
         void ForcedDisconnect(const QString & reason);
         void ShowFatalErrorMessage(const QString &);
         void GetRecord(RecordType type, const QByteArray & record);
         void CreateNetGame();
+        void PlayOfficialServerDemo();
         void UpdateWeapons();
         void DeleteWeapons(QString weaponsName);
         void AddWeapons(QString weaponsName, QString ammo);
         void EditWeapons(QString oldWeaponsName, QString newWeaponsName, QString ammo);
         void onFrontendFullscreen(bool value);
         void onFrontendEffects(bool value);
+        void onFrontendSoundsToggled(bool value);
         void Music(bool checked);
         void UpdateCampaignPage(int index);
         void UpdateCampaignPageTeam(int index);
         void UpdateCampaignPageProgress(int index);
         void UpdateCampaignPageMission(int index);
+        void UpdateTrainingPageTeam(int index);
         void InitCampaignPage();
+        void RestoreSingleplayerTeamSelection();
         void showFeedbackDialog();
         void showFeedbackDialogNetChecked();
 
@@ -155,7 +163,7 @@
         void FromNetProxySlot(const QByteArray &);
 
     private:
-        void _NetConnect(const QString & hostName, quint16 port, QString nick);
+        void _NetConnect(const QString & hostName, quint16 port, bool useTls, QString nick);
         int  AskForNickAndPwd(void);
         void UpdateTeamsLists();
         void CreateGame(GameCFGWidget * gamecfg, TeamSelWidget* pTeamSelWidget, QString ammo);
--- a/QTfrontend/main.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/main.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -138,6 +138,23 @@
     }
 }
 
+// Simple Message handler that suppresses Qt debug and info messages (qDebug, qInfo).
+// Used when printing command line help (--help) or related error to keep console clean.
+void restrictedMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+    Q_UNUSED(context)
+    QByteArray localMsg = msg.toLocal8Bit();
+    switch (type) {
+    case QtWarningMsg:
+    case QtCriticalMsg:
+    case QtFatalMsg:
+        fprintf(stderr, "%s\n", localMsg.constData());
+        break;
+    default:
+        break;
+    }
+}
+
 QString getUsage()
 {
     return QString(
@@ -150,39 +167,25 @@
 "\n"
 "%8"
 "\n"
-).arg(HWApplication::tr("Usage", "command-line"))
-.arg(HWApplication::tr("OPTION", "command-line"))
-.arg(HWApplication::tr("CONNECTSTRING", "command-line"))
-.arg(HWApplication::tr("Options", "command-line"))
-.arg(HWApplication::tr("Display this help", "command-line"))
+).arg(
+//: “Usage” as in “how the command-line syntax works”. Shown when running “hedgewars --help” in command-line
+HWApplication::tr("Usage", "command-line")
+).arg(
+//: Name of a command-line argument, shown when running “hedgewars --help” in command-line. “OPTION” as in “command-line option”
+HWApplication::tr("OPTION", "command-line")
+).arg(
+//: Name of a command-line argument, shown when running “hedgewars --help” in command-line
+HWApplication::tr("CONNECTSTRING", "command-line")
+).arg(
+//: “Options” as in “command-line options”
+HWApplication::tr("Options", "command-line")
+).arg(HWApplication::tr("Display this help", "command-line"))
 .arg(HWApplication::tr("Custom path for configuration data and user data", "command-line"))
 .arg(HWApplication::tr("Custom path to the game data folder", "command-line"))
 .arg(HWApplication::tr("Hedgewars can use a %1 (e.g. \"%2\") to connect on start.", "command-line").arg(HWApplication::tr("CONNECTSTRING", "command-line")).arg(QString("hwplay://") + NETGAME_DEFAULT_SERVER));
 }
 
 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,16 +194,27 @@
     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);
 
     // file engine, to be initialized later
     engine = NULL;
 
+    /*
+    This is for messages frelated to translatable command-line arguments.
+    If it is non-zero, will print out a message after loading locale
+    and exit.
+    */
+    enum cmdMsgStateEnum {
+        cmdMsgNone,
+        cmdMsgHelp,
+        cmdMsgMalformedArg,
+        cmdMsgUnknownArg,
+    };
+    enum cmdMsgStateEnum cmdMsgState = cmdMsgNone;
+    QString cmdMsgStateStr;
+
     // parse arguments
-
     QStringList arguments = app.arguments();
     QMap<QString, QString> parsedArgs;
     {
@@ -221,14 +235,17 @@
                 if(arg.startsWith("--")) {
                     if(arg == "--help")
                     {
-                        printf("%s", getUsage().toUtf8().constData());
-                        return 0;
+                        cmdMsgState = cmdMsgHelp;
+                        qInstallMessageHandler(restrictedMessageHandler);
                     }
-                    // argument is something wrong
-                    fprintf(stderr, "%s\n\n%s",
-                        HWApplication::tr("Malformed option argument: %1", "command-line").arg(arg).toUtf8().constData(),
-                        getUsage().toUtf8().constData());
-                    return 1;
+                    else
+                    {
+                        // argument is something wrong
+                        cmdMsgState = cmdMsgMalformedArg;
+                        cmdMsgStateStr = arg;
+                        qInstallMessageHandler(restrictedMessageHandler);
+                        break;
+                    }
                 }
 
                 // if not starting with --, then always skip
@@ -238,47 +255,61 @@
         }
     }
 
-    if(parsedArgs.contains("data-dir"))
+    if(cmdMsgState == cmdMsgNone)
     {
-        QFileInfo f(parsedArgs["data-dir"]);
-        parsedArgs.remove("data-dir");
-        if(!f.exists())
+        if(parsedArgs.contains("data-dir"))
+        {
+            QFileInfo f(parsedArgs["data-dir"]);
+            parsedArgs.remove("data-dir");
+            if(!f.exists())
+            {
+                qWarning() << "WARNING: Cannot open data-dir=" << f.absoluteFilePath();
+            }
+            *cDataDir = f.absoluteFilePath();
+            custom_data = true;
+        }
+
+        if(parsedArgs.contains("config-dir"))
+        {
+            QFileInfo f(parsedArgs["config-dir"]);
+            parsedArgs.remove("config-dir");
+            cfgdir->setPath(f.absoluteFilePath());
+            custom_config = true;
+        }
+        else
         {
-            qWarning() << "WARNING: Cannot open DATA_PATH=" << f.absoluteFilePath();
+            cfgdir->setPath(QDir::homePath());
+            custom_config = false;
+        }
+
+        if (!parsedArgs.isEmpty())
+        {
+            cmdMsgState = cmdMsgUnknownArg;
+            qInstallMessageHandler(restrictedMessageHandler);
         }
-        *cDataDir = f.absoluteFilePath();
-        custom_data = true;
+
+        // end of parameter parsing
+
+        // Select Qt style
+        QStyle* coreStyle;
+        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.");
+        }
     }
 
-    if(parsedArgs.contains("config-dir"))
-    {
-        QFileInfo f(parsedArgs["config-dir"]);
-        parsedArgs.remove("config-dir");
-        cfgdir->setPath(f.absoluteFilePath());
-        custom_config = true;
-    }
-    else
-    {
-        cfgdir->setPath(QDir::homePath());
-        custom_config = false;
-    }
-
-    if (!parsedArgs.isEmpty()) {
-        foreach (const QString & key, parsedArgs.keys())
-        {
-            fprintf(stderr, "%s\n", HWApplication::tr("Unknown option argument: %1", "command-line").arg(QString("--") + key).toUtf8().constData());
-        }
-        fprintf(stderr, "\n%s", getUsage().toUtf8().constData());
-        return 1;
-    }
-
-    // end of parameter parsing
-
-
 #ifdef Q_OS_WIN
+    // Splash screen for Windows
     QPixmap pixmap(":/res/splash.png");
     QSplashScreen splash(pixmap);
-    splash.show();
+    if(cmdMsgState == cmdMsgNone)
+    {
+        splash.show();
+    }
 #endif
 
     QDateTime now = QDateTime::currentDateTime();
@@ -297,10 +328,10 @@
         checkForDir(cfgdir->absolutePath() + "/Library/Application Support/Hedgewars");
         cfgdir->cd("Library/Application Support/Hedgewars");
 #elif defined _WIN32
-        char path[1024];
-        if(!SHGetFolderPathA(0, CSIDL_PERSONAL, NULL, 0, path))
+        wchar_t path[MAX_PATH];
+        if(SHGetFolderPathW(0, CSIDL_PERSONAL, NULL, 0, path) == S_OK)
         {
-            cfgdir->cd(path);
+            cfgdir->cd(QString::fromWCharArray(path));
             checkForDir(cfgdir->absolutePath() + "/Hedgewars");
             cfgdir->cd("Hedgewars");
         }
@@ -329,6 +360,7 @@
         checkForDir(cfgdir->absolutePath() + "/Logs");
         checkForDir(cfgdir->absolutePath() + "/Videos");
         checkForDir(cfgdir->absolutePath() + "/VideoTemp");
+        checkForDir(cfgdir->absolutePath() + "/VideoThumbnails");
     }
 
     datadir->cd(bindir->absolutePath());
@@ -351,27 +383,9 @@
 
     QTranslator TranslatorHedgewars;
     QTranslator TranslatorQt;
+    QSettings settings(DataManager::instance().settingsFileName(), QSettings::IniFormat);
+    settings.setIniCodec("UTF-8");
     {
-        QSettings settings(DataManager::instance().settingsFileName(), QSettings::IniFormat);
-        settings.setIniCodec("UTF-8");
-
-        // Heuristic to figure out if the user is (probably) a first-time player.
-        // If nickname is not set, then probably yes.
-        // The hidden setting firstLaunch is, if present, used to force HW to
-        // treat iself as if it were launched the first time.
-        QString nick = settings.value("net/nick", QString()).toString();
-        if (settings.contains("frontend/firstLaunch"))
-        {
-            isProbablyNewPlayer = settings.value("frontend/firstLaunch").toBool();
-        }
-        else
-        {
-            isProbablyNewPlayer = nick.isNull();
-        }
-
-        // Set firstLaunch to false to make sure we remember we have been launched before.
-        settings.setValue("frontend/firstLaunch", false);
-
         QString cc = settings.value("misc/locale", QString()).toString();
         if (cc.isEmpty())
         {
@@ -401,6 +415,54 @@
             app.installTranslator(&TranslatorQt);
         }
         app.setLayoutDirection(QLocale().textDirection());
+
+        // Handle command line messages
+        switch(cmdMsgState)
+        {
+            case cmdMsgHelp:
+            {
+                printf("%s", getUsage().toUtf8().constData());
+                return 0;
+            }
+            case cmdMsgMalformedArg:
+            {
+                fprintf(stderr, "%s\n\n%s",
+                    HWApplication::tr("Malformed option argument: %1", "command-line").arg(cmdMsgStateStr).toUtf8().constData(),
+                    getUsage().toUtf8().constData());
+                return 1;
+            }
+            case cmdMsgUnknownArg:
+            {
+                foreach (const QString & key, parsedArgs.keys())
+                {
+                    fprintf(stderr, "%s\n",
+                        HWApplication::tr("Unknown option argument: %1", "command-line").arg(QString("--") + key).toUtf8().constData());
+                }
+                fprintf(stderr, "\n%s", getUsage().toUtf8().constData());
+                return 1;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        // Heuristic to figure out if the user is (probably) a first-time player.
+        // If nickname is not set, then probably yes.
+        // The hidden setting firstLaunch is, if present, used to force HW to
+        // treat iself as if it were launched the first time.
+        QString nick = settings.value("net/nick", QString()).toString();
+        if (settings.contains("frontend/firstLaunch"))
+        {
+            isProbablyNewPlayer = settings.value("frontend/firstLaunch").toBool();
+        }
+        else
+        {
+            isProbablyNewPlayer = nick.isNull();
+        }
+
+        // Set firstLaunch to false to make sure we remember we have been launched before.
+        settings.setValue("frontend/firstLaunch", false);
     }
 
 #ifdef _WIN32
@@ -414,14 +476,21 @@
     }
 #endif
 
+    SDLInteraction::instance();
+
     QString style = "";
     QString fname;
 
-    checkSeason();
-    //For each season, there is an extra stylesheet
-    //Todo: change background for easter and birthday
-    //(simply replace res/BackgroundBirthday.png and res/BackgroundEaster.png
-    //with an appropriate background
+    bool holidaySilliness = settings.value("misc/holidaySilliness", true).toBool();
+    if(holidaySilliness)
+        checkSeason();
+    else
+        season = SEASON_NONE;
+
+    // For each season, there is an extra stylesheet.
+    // TODO: change background for easter
+    // (simply replace res/BackgroundEaster.png
+    // with an appropriate background).
     switch (season)
     {
         case SEASON_CHRISTMAS :
@@ -455,7 +524,8 @@
 
     app.form = new HWForm(NULL, style);
 #ifdef Q_OS_WIN
-    splash.finish(app.form);
+    if(cmdMsgState == cmdMsgNone)
+        splash.finish(app.form);
 #endif
     app.form->show();
 
@@ -466,6 +536,7 @@
         questionTutorialMsg.setIcon(QMessageBox::Question);
         questionTutorialMsg.setWindowTitle(QMessageBox::tr("Welcome to Hedgewars"));
         questionTutorialMsg.setText(QMessageBox::tr("Welcome to Hedgewars!\n\nYou seem to be new around here. Would you like to play some training missions first to learn the basics of Hedgewars?"));
+        questionTutorialMsg.setTextFormat(Qt::PlainText);
         questionTutorialMsg.setWindowModality(Qt::WindowModal);
         questionTutorialMsg.addButton(QMessageBox::Yes);
         questionTutorialMsg.addButton(QMessageBox::No);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/mission.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -0,0 +1,79 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2018 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "mission.h"
+#include "hwconsts.h"
+#include "DataManager.h"
+#include <QSettings>
+#include <QObject>
+#include <QLocale>
+
+QSettings* getMissionTeamFile(QString & missionName, QString & teamName)
+{
+    QSettings* teamfile = new QSettings(cfgdir->absolutePath() + "/Teams/" + teamName + ".hwt", QSettings::IniFormat, 0);
+    teamfile->setIniCodec("UTF-8");
+    if (!teamfile->childGroups().contains("Mission " + missionName) &&
+            teamfile->childGroups().contains("Mission " + missionName)){
+        teamfile->beginGroup("Mission " + missionName);
+        QStringList keys = teamfile->childKeys();
+        teamfile->endGroup();
+        for (int i=0;i<keys.size();i++) {
+            QVariant value = teamfile->value("Mission " + missionName + "/" + keys[i]);
+            teamfile->setValue("Mission " + missionName + "/" + keys[i], value);
+        }
+        teamfile->remove("Mission " + missionName);
+    }
+
+    return teamfile;
+}
+
+/**
+    Returns true if the specified mission has been completed
+    missionName: Name of the mission in question
+    teamName: Name of the playing team
+*/
+bool isMissionWon(QString & missionName, QString & teamName)
+{
+    QSettings* teamfile = getMissionTeamFile(missionName, teamName);
+    bool won = teamfile->value("Mission " + missionName + "/Won", false).toBool();
+    return won;
+}
+
+/**
+    Returns true if the mission value adressed with the provided
+    missionName: Name of the mission in question
+    teamName: Name of the playing team
+    key: name of key to check
+*/
+bool missionValueExists(QString & missionName, QString & teamName, QString key)
+{
+    QSettings* teamfile = getMissionTeamFile(missionName, teamName);
+    return teamfile->contains("Mission " + missionName + "/" + key);
+}
+/**
+    Returns a mission value.
+    NOTE: Check whether the mission value exists first, using missionValueExists.
+    missionName: Name of the mission in question
+    teamName: Name of the playing team
+    key: name of key to read its value from
+*/
+QVariant getMissionValue(QString & missionName, QString & teamName, QString key)
+{
+    QSettings* teamfile = getMissionTeamFile(missionName, teamName);
+    return teamfile->value("Mission " + missionName + "/" + key);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/mission.h	Wed Jul 31 23:14:27 2019 +0200
@@ -0,0 +1,30 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2018 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef MISSION_H
+#define MISSION_H
+
+#include <QString>
+#include <QSettings>
+
+QSettings* getMissionTeamFile(QString & missionName, QString & teamName);
+bool isMissionWon(QString & missionName, QString & teamName);
+bool missionValueExists(QString & missionName, QString & teamName, QString key);
+QVariant getMissionValue(QString & missionName, QString & teamName, QString key);
+
+#endif
--- a/QTfrontend/model/ThemeFilterProxyModel.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/model/ThemeFilterProxyModel.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -29,6 +29,7 @@
 {
     isFilteringDLC = false;
     isFilteringHidden = false;
+    isFilteringBackground = false;
 }
 
 bool ThemeFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const
@@ -43,13 +44,15 @@
         searchOkay = in != -1;
     }
 
-    if(isFilteringDLC || isFilteringHidden)
+    if(isFilteringDLC || isFilteringHidden || isFilteringBackground)
     {
         bool isDLC = index.data(ThemeModel::IsDlcRole).toBool();
         bool isHidden = index.data(ThemeModel::IsHiddenRole).toBool();
+        bool isBackground = index.data(ThemeModel::IsBackgroundThemeRole).toBool();
 
         return ( ((isFilteringDLC && !isDLC) || !isFilteringDLC) &&
-                 ((isFilteringHidden && !isHidden) || !isFilteringHidden) ) &&
+                 ((isFilteringHidden && !isHidden) || !isFilteringHidden) &&
+                 ((isFilteringBackground && !isBackground) || !isFilteringBackground) ) &&
                searchOkay;
     }
     else
@@ -69,3 +72,9 @@
     isFilteringHidden = enable;
     invalidateFilter();
 }
+
+void ThemeFilterProxyModel::setFilterBackground(bool enable)
+{
+    isFilteringBackground = enable;
+    invalidateFilter();
+};
--- a/QTfrontend/model/ThemeFilterProxyModel.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/model/ThemeFilterProxyModel.h	Wed Jul 31 23:14:27 2019 +0200
@@ -37,6 +37,7 @@
         ThemeFilterProxyModel(QObject *parent = 0);
         void setFilterDLC(bool enabled);
         void setFilterHidden(bool enabled);
+        void setFilterBackground(bool enabled);
 
     protected:
         bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
@@ -44,6 +45,7 @@
     private:
         bool isFilteringDLC;
         bool isFilteringHidden;
+        bool isFilteringBackground;
 };
 
 #endif // HEDGEWARS_THEMEFILTERPROXYMODEL_H
--- a/QTfrontend/model/ThemeModel.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/model/ThemeModel.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -124,38 +124,49 @@
     {
         QMap<int, QVariant> dataset;
 
+        // Ignore directories without theme.cfg
+        QFile themeCfgFile(QString("physfs://Themes/%1/theme.cfg").arg(theme));
+        if (!themeCfgFile.open(QFile::ReadOnly))
+        {
+            continue;
+        }
+
         // themes without icon are supposed to be hidden
         QString iconpath = QString("physfs://Themes/%1/icon.png").arg(theme);
-
         if (!QFile::exists(iconpath))
         {
             dataset.insert(IsHiddenRole, true);
         }
         else
         {
-            // themes with the key “hidden” in theme.cfg are hidden, too
-            QFile themeCfgFile(QString("physfs://Themes/%1/theme.cfg").arg(theme));
-            if (themeCfgFile.open(QFile::ReadOnly))
+            QTextStream stream(&themeCfgFile);
+            QString line = stream.readLine();
+            QString key;
+            while (!line.isNull())
             {
-                QTextStream stream(&themeCfgFile);
-                QString line = stream.readLine();
-                QString key;
-                while (!line.isNull())
+                key = QString(line);
+                int equalsPos = line.indexOf('=');
+                key.truncate(equalsPos - 1);
+                key = key.simplified();
+                if (!line.startsWith(';') && key == "hidden")
                 {
-                    key = QString(line);
-                    int equalsPos = line.indexOf('=');
-                    key.truncate(equalsPos - 1);
-                    key = key.simplified();
-                    if (!line.startsWith(';') && key == "hidden")
-                    {
-                        dataset.insert(IsHiddenRole, true);
-                        break;
-                    }
-                    line = stream.readLine();
+                    dataset.insert(IsHiddenRole, true);
+                    break;
                 }
+                line = stream.readLine();
             }
         }
 
+        // Themes without land textures are considered "background themes"
+        // since they cannot be used for generated maps, but they can be used
+        // for image maps.
+        QString landtexpath = QString("physfs://Themes/%1/LandTex.png").arg(theme);
+        QString bordertexpath = QString("physfs://Themes/%1/Border.png").arg(theme);
+        if ((!QFile::exists(landtexpath)) || (!QFile::exists(bordertexpath)))
+        {
+            dataset.insert(IsBackgroundThemeRole, true);
+        }
+
         // detect if theme is dlc
         QString themeDir = PHYSFS_getRealDir(QString("Themes/%1").arg(theme).toLocal8Bit().data());
         bool isDLC = !themeDir.startsWith(datadir->absolutePath());
@@ -179,5 +190,6 @@
         }
 
         m_data.append(dataset);
+        themeCfgFile.close();
     }
 }
--- a/QTfrontend/model/ThemeModel.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/model/ThemeModel.h	Wed Jul 31 23:14:27 2019 +0200
@@ -41,7 +41,7 @@
         Q_OBJECT
 
     public:
-        enum Roles { ActualNameRole = Qt::UserRole, IsDlcRole, IconPathRole, IsHiddenRole };
+        enum Roles { ActualNameRole = Qt::UserRole, IsDlcRole, IconPathRole, IsHiddenRole, IsBackgroundThemeRole };
         explicit ThemeModel(QObject *parent = 0);
 
         int rowCount(const QModelIndex &parent = QModelIndex()) const;
--- a/QTfrontend/model/gameSchemeModel.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/model/gameSchemeModel.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -87,7 +87,9 @@
                          << "Timeless"
                          << "Thinking with Portals"
                          << "King Mode"
+                         << "Mutant"
                          << "Construction Mode"
+                         << "The Specialists"
                          << "Space Invasion"
                          << "HedgeEditor"
                          ;
@@ -573,9 +575,57 @@
             << QVariant()              // scriptparam    43
             ;
 
+    QList<QVariant> mutant;
+    mutant
+            << predefSchemesNames[10]  // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(true)          // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(20)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(2)             // case prob      30
+            << QVariant(1)             // mines time     31
+            << QVariant(4)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(2)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(0)             // health case pct 36
+            << QVariant(25)            // 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<QVariant> construction;
     construction
-            << predefSchemesNames[10]  // name           0
+            << predefSchemesNames[11]  // name           0
             << QVariant(false)         // switchhog      1
             << QVariant(false)         // team divide    2
             << QVariant(false)         // solid land     3
@@ -622,9 +672,58 @@
             << QVariant("initialenergy=550, energyperround=50, maxenergy=1000, cratesperround=5") // scriptparam    43
             ;
 
+    QList<QVariant> specialists;
+    specialists
+            << predefSchemesNames[12]  // name           0
+            << QVariant(true)          // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(false)         // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(true)          // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(true)          // inf. attack    19
+            << QVariant(true)          // reset weps     20
+            << QVariant(true)          // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(45)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(5)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(0)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(0)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(100)           // 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
+            // NOTE: If you change this, also change the defaults in the The Specialists script
+            << QVariant("t=SENDXHPL")  // scriptparam    43
+            ;
+
     QList<QVariant> spaceinvasion;
     spaceinvasion
-            << predefSchemesNames[11]  // name           0
+            << predefSchemesNames[13]  // name           0
             << QVariant(false)         // switchhog      1
             << QVariant(false)         // team divide    2
             << QVariant(false)         // solid land     3
@@ -673,7 +772,7 @@
 
     QList<QVariant> hedgeeditor;
     hedgeeditor
-            << predefSchemesNames[12]  // name           0
+            << predefSchemesNames[14]  // name           0
             << QVariant(false)         // switchhog      1
             << QVariant(false)         // team divide    2
             << QVariant(false)         // solid land     3
@@ -731,13 +830,20 @@
     schemes.append(timeless);
     schemes.append(thinkingportals);
     schemes.append(kingmode);
+    schemes.append(mutant);
     schemes.append(construction);
+    schemes.append(specialists);
     schemes.append(spaceinvasion);
     schemes.append(hedgeeditor);
 
     if (!QDir(cfgdir->absolutePath() + "/Schemes").exists()) {
         QDir().mkdir(cfgdir->absolutePath() + "/Schemes");
     }
+    QStringList predefSchemesNamesLower;
+    for (int i = 0; i < predefSchemesNames.size(); ++i)
+    {
+        predefSchemesNamesLower.append(predefSchemesNames[i].toLower());
+    }
     if (!QDir(directory).exists()) {
         QDir().mkdir(directory);
 
@@ -751,7 +857,7 @@
             legacyFileConfig.setArrayIndex(i);
 
             QString schemeName = legacyFileConfig.value(spNames[0]).toString();
-            if (!schemeName.isNull() && !predefSchemesNames.contains(schemeName))
+            if (!schemeName.isNull() && !predefSchemesNamesLower.contains(schemeName.toLower()))
             {
                 QList<QVariant> scheme;
                 QFile file(directory + "/" + schemeName + ".hwg");
@@ -794,6 +900,11 @@
             if (schemeName.endsWith(".hwg", Qt::CaseInsensitive)) {
                 schemeName.chop(4);
             }
+            // Don't load scheme if name collides with default scheme
+            if (predefSchemesNamesLower.contains(schemeName.toLower())) {
+                qWarning("Game scheme \"%s\" not loaded from file, name collides with a default scheme!", qPrintable(schemeName));
+                continue;
+            }
             // Parse game scheme file
             if (file.open(QIODevice::ReadOnly)) {
                 QTextStream stream(&file);
@@ -856,9 +967,15 @@
 
 bool GameSchemeModel::hasScheme(QString name)
 {
+    return hasScheme(name, -1);
+}
+
+bool GameSchemeModel::hasScheme(QString name, int ignoreID)
+{
+    QString nameLower = name.toLower();
     for(int i=0; i<schemes.size(); i++)
     {
-        if(schemes[i][0] == name)
+        if(((ignoreID == -1) || (i != ignoreID)) && (schemes[i][0].toString().toLower() == nameLower))
         {
             return true;
         }
@@ -866,6 +983,11 @@
     return false;
 }
 
+bool GameSchemeModel::renameScheme(int index, QString newName)
+{
+    return setData(QAbstractItemModel::createIndex(index, 0), QVariant(newName));
+}
+
 Qt::ItemFlags GameSchemeModel::flags(const QModelIndex & index) const
 {
     Q_UNUSED(index);
--- a/QTfrontend/model/gameSchemeModel.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/model/gameSchemeModel.h	Wed Jul 31 23:14:27 2019 +0200
@@ -34,6 +34,8 @@
         int rowCount(const QModelIndex & parent) const;
         int columnCount(const QModelIndex & parent) const;
         bool hasScheme(QString name);
+        bool hasScheme(QString name, int ignoreID);
+        bool renameScheme(int index, QString newName);
         Qt::ItemFlags flags(const QModelIndex & index) const;
         bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
         bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex());
@@ -47,6 +49,9 @@
     public slots:
         void Save();
 
+    signals:
+        void dataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
+
     protected:
         QList< QList<QVariant> > schemes;
 };
--- a/QTfrontend/model/netserverslist.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/model/netserverslist.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -45,8 +45,10 @@
             case 0:
                 return tr("Title");
             case 1:
+                //: short for "IP address" (Internet Protocol), part of server address
                 return tr("IP");
             case 2:
+                //: short for "port number", part of server address
                 return tr("Port");
             default:
                 return QVariant();
--- a/QTfrontend/net/hwmap.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/hwmap.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -25,7 +25,7 @@
 #include "hwmap.h"
 
 HWMap::HWMap(QObject * parent) :
-    TCPBase(false, parent)
+    TCPBase(false, false, parent)
 {
     templateFilter = 0;
     m_mapgen = MAPGEN_REGULAR;
--- a/QTfrontend/net/hwmapoptimizer.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/hwmapoptimizer.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -2,7 +2,7 @@
 #include "hwconsts.h"
 
 HWMapOptimizer::HWMapOptimizer(QObject *parent) :
-    TCPBase(parent)
+    TCPBase(false, false, parent)
 {
 }
 
--- a/QTfrontend/net/newnetclient.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/newnetclient.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -41,6 +41,7 @@
 {
     m_private_game = false;
     m_nick_registered = false;
+    m_demo_data_pending = false;
 
     m_roomsListModel = new RoomsListModel(this);
 
@@ -74,24 +75,35 @@
 {
     if (m_game_connected)
     {
-        RawSendNet(QString("QUIT%1%2").arg(delimiter).arg("User quit"));
-        emit disconnected(tr("User quit"));
+        RawSendNet(QString("QUIT%1").arg(delimiter));
+        emit disconnected("");
     }
     NetSocket.flush();
 }
 
-void HWNewNet::Connect(const QString & hostName, quint16 port, const QString & nick)
+void HWNewNet::Connect(const QString & hostName, quint16 port, bool useTls, const QString & nick)
 {
     netClientState = Connecting;
     mynick = nick;
     myhost = hostName + QString(":%1").arg(port);
-    NetSocket.connectToHost(hostName, port);
+    if (useTls)
+    {
+        NetSocket.connectToHostEncrypted(hostName, port);
+        if (!NetSocket.waitForEncrypted())
+        {
+            qWarning("Handshake failed");
+        }
+    }
+    else 
+    {
+        NetSocket.connectToHost(hostName, port);
+    }
 }
 
 void HWNewNet::Disconnect()
 {
     if (m_game_connected)
-        RawSendNet(QString("QUIT%1%2").arg(delimiter).arg("User quit"));
+        RawSendNet(QString("QUIT%1").arg(delimiter));
     m_game_connected = false;
 
     NetSocket.disconnectFromHost();
@@ -237,7 +249,12 @@
             emit disconnected(tr("The host was not found. Please check the host name and port settings."));
             break;
         case QAbstractSocket::ConnectionRefusedError:
-            emit disconnected(tr("Connection refused"));
+            if (getHost() == (QString("%1:%2").arg(NETGAME_DEFAULT_SERVER).arg(NETGAME_DEFAULT_PORT)))
+                // Error for official server
+                emit disconnected(tr("The connection was refused by the official server or timed out. Something seems to be wrong with the official server at the moment. This might be a temporary problem. Please try again later."));
+            else
+                // Error for every other host
+                emit disconnected(tr("The connection was refused by the host or timed out. This might have one of the following reasons:\n- The Hedgewars Server program does currently not run on the host\n- The specified port number is incorrect\n- There is a temporary network problem\n\nPlease check the host name and port settings and/or try again later."));
             break;
         default:
             emit disconnected(NetSocket.errorString());
@@ -252,6 +269,17 @@
     maybeSendPassword();
 }
 
+void HWNewNet::ContinueConnection()
+{
+    if (netClientState == Connected)    
+    {
+        RawSendNet(QString("NICK%1%2").arg(delimiter).arg(mynick));
+        RawSendNet(QString("PROTO%1%2").arg(delimiter).arg(*cProtoVer));    
+        m_game_connected = true;
+        emit adminAccess(false);
+    }
+}
+
 void HWNewNet::ParseCmd(const QStringList & lst)
 {
     qDebug() << "Server: " << lst;
@@ -291,6 +319,27 @@
         return;
     }
 
+    if (lst[0] == "REDIRECT")
+    {        
+        if (lst.size() < 2 || lst[1].toInt() == 0)
+        {
+            qWarning("Net: Malformed REDIRECT message");
+            return;            
+        }
+
+        quint16 port = lst[1].toInt();
+        if (port == 0) 
+        {
+            qWarning() << "Invalid redirection port";            
+        }
+        else 
+        {
+            netClientState = Redirected;
+            emit redirected(port);
+        }
+        return;
+    }
+
     if (lst[0] == "CONNECTED")
     {
         if(lst.size() < 3 || lst[2].toInt() < cMinServerVersion)
@@ -303,11 +352,12 @@
             return;
         }
 
-        RawSendNet(QString("NICK%1%2").arg(delimiter).arg(mynick));
-        RawSendNet(QString("PROTO%1%2").arg(delimiter).arg(*cProtoVer));
-        netClientState = Connected;
-        m_game_connected = true;
-        emit adminAccess(false);
+        ClientState lastState = netClientState;
+        netClientState = Connected;      
+        if (lastState != Redirected)
+        {
+            ContinueConnection();
+        }
         return;
     }
 
@@ -376,22 +426,50 @@
             return;
         }
 
-        QString action = HWProto::chatStringToAction(lst[2]);
+        QString action;
+        QString message;
+        QString sender = lst[1];
+        // '[' is a special character used in fake nick names of server messages.
+        // Those are supposed to be translated
+        if(!sender.startsWith('['))
+        {
+            // Normal message
+            message = lst[2];
+            // Another kind of fake nick. '(' nicks are server messages, but they must not be translated
+            if(!sender.startsWith('('))
+            {
+                // Check for action (/me command)
+                action = HWProto::chatStringToAction(message);
+            }
+            else
+            {
+                // If parenthesis were used, replace them with square brackets
+                // for a consistent style.
+                sender.replace(0, 1, '[');
+                sender.replace(sender.length()-1, 1, ']');
+            }
+        }
+        else
+        {
+            // Server message
+            // Server messages are translated client-side
+            message = HWApplication::translate("server", lst[2].toLatin1().constData());
+        }
 
         if (netClientState == InLobby)
         {
-            if (action != NULL)
-                emit lobbyChatAction(lst[1], action);
+            if (!action.isNull())
+                emit lobbyChatAction(sender, action);
             else
-                emit lobbyChatMessage(lst[1], lst[2]);
+                emit lobbyChatMessage(sender, message);
         }
         else
         {
-            emit chatStringFromNet(HWProto::formatChatMsg(lst[1], lst[2]));
-            if (action != NULL)
-                emit roomChatAction(lst[1], action);
+            emit chatStringFromNet(HWProto::formatChatMsg(sender, message));
+            if (!action.isNull())
+                emit roomChatAction(sender, action);
             else
-                emit roomChatMessage(lst[1], lst[2]);
+                emit roomChatMessage(sender, message);
         }
         return;
     }
@@ -721,13 +799,23 @@
         return;
     }
 
-    if(netClientState == InRoom || netClientState == InGame)
+    if(netClientState == InLobby && lst[0] == "REPLAY_START")
+    {
+        netClientState = InRoom;
+        m_demo_data_pending = true;
+        emit EnteredGame();
+        emit roomMaster(false);
+        return;
+    }
+
+    if(netClientState == InRoom || netClientState == InGame || netClientState == InDemo)
     {
         if (lst[0] == "EM")
         {
             if(lst.size() < 2)
             {
                 qWarning("Net: Bad EM message");
+                m_demo_data_pending = false;
                 return;
             }
             for(int i = 1; i < lst.size(); ++i)
@@ -735,9 +823,13 @@
                 QByteArray em = QByteArray::fromBase64(lst[i].toLatin1());
                 emit FromNet(em);
             }
+            m_demo_data_pending = false;
             return;
         }
+    }
 
+    if(netClientState == InRoom || netClientState == InGame)
+    {
         if (lst[0] == "ROUND_FINISHED")
         {
             emit FromNet(QByteArray("\x01o"));
@@ -779,8 +871,16 @@
 
         if (lst[0] == "RUN_GAME")
         {
-            netClientState = InGame;
-            emit AskForRunGame();
+            if(m_demo_data_pending)
+            {
+                netClientState = InDemo;
+                emit AskForOfficialServerDemo();
+            }
+            else
+            {
+                netClientState = InGame;
+                emit AskForRunGame();
+            }
             return;
         }
 
@@ -865,7 +965,10 @@
             if (lst.size() < 3)
                 emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
             else
-                emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
+            {
+                QString leaveMsg = QString(lst[2]);
+                emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1]).arg(HWApplication::translate("server", leaveMsg.toLatin1().constData())));
+            }
             m_playersModel->playerLeftRoom(lst[1]);
             return;
         }
@@ -985,6 +1088,13 @@
         netClientState = InRoom;
         RawSendNet(QString("ROUNDFINISHED%1%2").arg(delimiter).arg(correctly ? "1" : "0"));
     }
+    else if (netClientState == InDemo)
+    {
+        netClientState = InLobby;
+        askRoomsList();
+        emit LeftRoom(QString());
+        m_playersModel->resetRoomFlags();
+    }
 }
 
 void HWNewNet::banPlayer(const QString & nick)
--- a/QTfrontend/net/newnetclient.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/newnetclient.h	Wed Jul 31 23:14:27 2019 +0200
@@ -22,7 +22,7 @@
 
 #include <QObject>
 #include <QString>
-#include <QTcpSocket>
+#include <QSslSocket>
 #include <QMap>
 
 #include "team.h"
@@ -43,11 +43,12 @@
         Q_OBJECT
 
     public:
-        enum ClientState { Disconnected, Connecting, Connected, InLobby, InRoom, InGame };
+        enum ClientState { Disconnected, Connecting, Redirected, Connected, InLobby, InRoom, InGame, InDemo };
 
         HWNewNet();
         ~HWNewNet();
-        void Connect(const QString & hostName, quint16 port, const QString & nick);
+        void Connect(const QString & hostName, quint16 port, bool useTls, const QString & nick);
+        void ContinueConnection();
         void Disconnect();
         void SendPasswordHash(const QString & hash);
         void NewNick(const QString & nick);
@@ -68,10 +69,11 @@
         QString mynick;
         QString myroom;
         QString myhost;
-        QTcpSocket NetSocket;
+        QSslSocket NetSocket;
         QString seed;
         bool m_game_connected;
         bool m_nick_registered;
+        bool m_demo_data_pending;
         RoomsListModel * m_roomsListModel;
         PlayersListModel * m_playersModel;
         QSortFilterProxyModel * m_lobbyPlayersModel;
@@ -87,7 +89,7 @@
         int  ByteLength(const QString & str);
         void RawSendNet(const QString & buf);
         void RawSendNet(const QByteArray & buf);
-        void ParseCmd(const QStringList & lst);
+        void ParseCmd(const QStringList & lst);        
         void handleNotice(int n);
 
         void maybeSendPassword();
@@ -96,8 +98,10 @@
 
     signals:
         void AskForRunGame();
+        void AskForOfficialServerDemo();
         void connected();
         void disconnected(const QString & reason);
+        void redirected(quint16 port);
         void Error(const QString & errmsg);
         void Warning(const QString & wrnmsg);
         void NickRegistered(const QString & nick);
--- a/QTfrontend/net/proto.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/proto.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -47,8 +47,11 @@
 
 QString HWProto::formatChatMsg(const QString & nick, const QString & msg)
 {
-    if(msg.left(4) == "/me ")
+    // Messages using the /me command.
+    // Server messages (nick starts with a bracket) are never considered /me messages.
+    if(msg.left(4) == "/me " && (!nick.startsWith('[')) && (!nick.startsWith('(')))
         return QString("\x02* %1 %2").arg(nick).arg(msg.mid(4));
+    // Normal chat message
     else
         return QString("\x01%1: %2").arg(nick).arg(msg);
 }
--- a/QTfrontend/net/recorder.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/recorder.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -23,6 +23,7 @@
 #include "gameuiconfig.h"
 #include "hwconsts.h"
 #include "game.h"
+#include "util/MessageDialog.h"
 #include "LibavInteraction.h"
 
 // Encoding is memory expensive process, so we need to limit maximum number
@@ -33,12 +34,13 @@
 static QList<HWRecorder*> queue;
 
 HWRecorder::HWRecorder(GameUIConfig * config, const QString &prefix) :
-    TCPBase(false)
+    TCPBase(false, !config->language().isEmpty())
 {
     this->config = config;
     this->prefix = prefix;
     item = 0;
     finished = false;
+    aborted = false;
     name = prefix + "." + LibavInteraction::instance().getExtension(config->AVFormat());
 }
 
@@ -75,6 +77,16 @@
         case 'v':
             finished = true;
             break;
+        case 'E':
+            int size = msg.size();
+            emit ErrorMessage(
+                tr("A fatal ERROR occured while processing the video recording! "
+                "The video could not be saved.\n\n"
+                "As a workaround, you could try to reset the Hedgewars video recorder settings to the defaults.\n\n"
+                "To report this error, please click the 'Feedback' button in the main menu!\n\n"
+                "Last engine message:\n%1")
+                .arg(QString::fromUtf8(msg.mid(2).left(size - 4))));
+            return;
         }
     }
 }
@@ -140,7 +152,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";
     arguments << "--chat-size";
     arguments << QString::number(config->chatSize());
 
@@ -151,3 +166,10 @@
 {
     return true;
 }
+
+void HWRecorder::abort()
+{
+    queue.removeOne(this);
+    aborted = true;
+    deleteLater();
+}
--- a/QTfrontend/net/recorder.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/recorder.h	Wed Jul 31 23:14:27 2019 +0200
@@ -35,6 +35,7 @@
         virtual ~HWRecorder();
 
         void EncodeVideo(const QByteArray & record);
+        void abort();
         bool simultaneousRun();
 
         VideoItem * item; // used by pagevideos
@@ -50,9 +51,11 @@
     signals:
         void onProgress(float progress); // 0 < progress < 1
         void encodingFinished(bool success);
+        void ErrorMessage(const QString &);
 
     private:
         bool finished;
+        bool aborted;
         GameUIConfig * config;
 };
 
--- a/QTfrontend/net/tcpBase.cpp	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/tcpBase.cpp	Wed Jul 31 23:14:27 2019 +0200
@@ -21,10 +21,12 @@
 #include <QImage>
 #include <QThread>
 #include <QApplication>
+#include <QProcessEnvironment>
 
 #include "tcpBase.h"
 #include "hwconsts.h"
 #include "MessageDialog.h"
+#include "gameuiconfig.h"
 
 #ifdef HWLIBRARY
 extern "C" {
@@ -104,11 +106,12 @@
 
 }
 
-TCPBase::TCPBase(bool demoMode, QObject *parent) :
+TCPBase::TCPBase(bool demoMode, bool usesCustomLanguage, QObject *parent) :
     QObject(parent),
     m_hasStarted(false),
     m_isDemoMode(demoMode),
     m_connected(false),
+    m_usesCustomLanguage(usesCustomLanguage),
     IPCSocket(0)
 {
     process = 0;
@@ -183,6 +186,18 @@
     process->setProcessChannelMode(QProcess::ForwardedChannels);
 #endif
 
+    // If game config uses non-system locale, we set the environment
+    // of the engine first
+    if(m_usesCustomLanguage)
+    {
+        QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+        QString hwengineLang = QLocale().name() + ".UTF8";
+        qDebug("Setting hwengine environment: LANG=%s", qPrintable(hwengineLang));
+        // TODO: Check if this is correct and works on all systems
+        env.insert("LANG", QLocale().name() + ".UTF8");
+        process->setProcessEnvironment(env);
+    }
+    qDebug("Starting hwengine ...");
     process->start(bindir->absolutePath() + "/hwengine", arguments);
 #endif
     m_hasStarted = true;
@@ -235,7 +250,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(
@@ -244,16 +259,22 @@
             "We are very sorry for the inconvenience :(\n\n"
             "If this keeps happening, please click the '%2' button in the main menu!")
             .arg(exitCode)
-            .arg("Feedback"));
+            .arg(QCoreApplication::translate("PageMain", "Feedback")));
 
     }
 }
 
 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)
--- a/QTfrontend/net/tcpBase.h	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/net/tcpBase.h	Wed Jul 31 23:14:27 2019 +0200
@@ -41,7 +41,7 @@
         Q_OBJECT
 
     public:
-        TCPBase(bool demoMode, QObject * parent = 0);
+        TCPBase(bool demoMode, bool usesCustomLanguage, QObject * parent = 0);
         virtual ~TCPBase();
 
         virtual bool couldBeRemoved();
@@ -80,6 +80,7 @@
 #endif
         bool m_isDemoMode;
         bool m_connected;
+        bool m_usesCustomLanguage;
         void RealStart();
         QPointer<QTcpSocket> IPCSocket;
 
Binary file QTfrontend/res/AboutIcon.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/res/AboutIcon.svg	Wed Jul 31 23:14:27 2019 +0200
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   height="32"
+   width="32"
+   version="1.1"
+   id="svg2">
+  <metadata
+     id="metadata8">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs6" />
+  <g
+     transform="matrix(-0.87631421,0.03741655,-0.03741655,-0.87631421,30.49124,29.594247)"
+     id="g3029">
+    <path
+       transform="rotate(3.2786745,123.04143,146.57831)"
+       id="path3005"
+       style="font-weight:bold;-inkscape-font-specification:'Blue Highway Bold';fill:#ffd200;fill-opacity:1;stroke:none;marker:none"
+       d="m 11.303256,19.777238 c -0.03334,0.33335 -0.221629,1.313768 -0.2,2.95 l 0.04383,3.315682 c 0.02469,1.867793 -5.2572313,1.747817 -5.2827504,-0.185783 l -0.042458,-3.217073 c -0.016299,-1.235012 -0.1724587,-2.214142 -0.2,-2.95 L 5.2166164,8.8621882 C 5.1452969,6.9566491 10.688148,6.9046651 10.797715,9.1976297 L 11.303252,19.777238 M 10.475624,36.052449 C 9.9512694,36.590344 9.1763118,36.97929 8.4251706,36.98741 7.571138,36.996642 6.668651,36.585565 6.0714109,35.975023 c -0.5908326,-0.603991 -0.9531814,-1.505086 -0.95,-2.35 0.00312,-0.829485 0.3773402,-1.699903 0.95,-2.3 0.5338724,-0.559451 1.3271485,-0.982489 2.1004527,-0.984961 0.8456366,-0.0027 1.7124043,0.457898 2.3037604,1.062387 0.584611,0.597594 0.996737,1.464011 1,2.3 0.0033,0.8513 -0.405757,1.740412 -1,2.35 z" />
+    <path
+       id="path3077-8"
+       d="m 15.124551,30.576638 c 0.772274,0.03998 1.590525,-0.319405 2.154304,-0.848705 0.60474,-0.567755 1.039924,-1.424446 1.088575,-2.252508 0.04956,-0.843466 -0.292495,-1.768789 -0.849279,-2.404305 -0.391658,-0.447041 -0.919699,-0.775772 -1.502223,-0.965299 0.342102,0.61227 0.566629,1.320301 0.535846,1.998773 -0.03759,0.828639 -0.483835,1.684754 -1.088575,2.25251 -0.563779,0.529298 -1.383763,0.914063 -2.154304,0.848704 -0.237104,-0.02013 -0.462709,-0.06785 -0.687858,-0.154669 0.08177,0.120329 0.157133,0.23628 0.25079,0.343174 0.331909,0.378819 0.778394,0.717828 1.252152,0.934624 0.126359,0.05142 0.264219,0.0928 0.375358,0.155388 0.206233,0.06367 0.414086,0.08138 0.625214,0.09231 z"
+       style="font-weight:bold;-inkscape-font-specification:'Blue Highway Bold';fill:#ff9c00;fill-opacity:1;stroke:none;marker:none" />
+    <path
+       id="path3077"
+       d="m 16.118586,21.50724 c 1.185947,-0.105017 2.140919,-0.508736 2.153443,-1.223703 l 0.06586,-9.517778 c 0.0129,-0.736262 -0.09761,-1.7037417 -0.038,-2.9374217 l 0.238359,-5.063061 c 0.03988,-0.8254491 -0.907744,-1.347129 -2.003587,-1.5578984 l -0.238649,4.9380614 c -0.0596,1.233681 0.0509,2.20116 0.038,2.937421 l -0.09214,9.7668847 c -0.02067,1.180155 -1.569508,1.168975 -3.498387,0.709355 -0.06542,1.381819 1.421129,1.94814 3.375093,1.94814 z"
+       style="font-weight:bold;-inkscape-font-specification:'Blue Highway Bold';fill:#ff9c00;fill-opacity:1;stroke:none;marker:none" />
+    <path
+       d="m 11.103256,22.727238 0.04544,3.426332 c 0.02477,1.867791 -5.2571451,1.747815 -5.2827511,-0.185783 L 5.821879,22.640064 C 5.8055242,21.405053 5.88445,20.378434 5.864278,19.642338 L 5.5717401,8.9675172 C 5.5195033,7.0613596 10.549292,7.3739262 10.636267,9.6678589 l 0.341581,9.0091431 c 0.07451,1.531058 0.120498,3.407284 0.125408,4.050236 z M 10.475624,36.052449 C 9.9512694,36.590344 9.1763118,36.97929 8.4251706,36.98741 7.571138,36.996642 6.668651,36.585565 6.0714109,35.975023 c -0.5908326,-0.603991 -0.9531814,-1.505086 -0.95,-2.35 0.00312,-0.829485 0.3773402,-1.699903 0.95,-2.3 0.5338724,-0.559451 1.3271485,-0.982489 2.1004527,-0.984961 0.8456366,-0.0027 1.7124043,0.457898 2.3037604,1.062387 0.584611,0.597594 0.996737,1.464011 1,2.3 0.0033,0.8513 -0.405757,1.740412 -1,2.35 z"
+       style="color:#000000;font-weight:bold;-inkscape-font-specification:'Blue Highway Bold';display:inline;overflow:visible;visibility:visible;fill:none;stroke:#120d02;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+       id="path3007"
+       transform="rotate(3.2786745,123.04143,146.57831)" />
+  </g>
+</svg>
Binary file QTfrontend/res/Challenges.png has changed
Binary file QTfrontend/res/Challenges.xcf has changed
Binary file QTfrontend/res/Help.png has changed
Binary file QTfrontend/res/Scenarios.png has changed
Binary file QTfrontend/res/StatsEverAfter.png has changed
Binary file QTfrontend/res/StatsH.png has changed
Binary file QTfrontend/res/Trainings.png has changed
Binary file QTfrontend/res/Trainings_en.png has changed
Binary file QTfrontend/res/btnTagTeam.png has changed
Binary file QTfrontend/res/btnTagTeam@2x.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/backstab.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/dragon.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/enemy.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/epil.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/family.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/first_blood.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/journey.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/queen.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/shadow.png has changed
Binary file QTfrontend/res/campaign/A_Classic_Fairytale/united.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/cosmos.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/death01.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/death02.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/desert01.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/desert02.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/desert03.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/final.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/fruit01.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/fruit02.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/fruit03.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/ice01.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/ice02.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/moon01.png has changed
Binary file QTfrontend/res/campaign/A_Space_Adventure/moon02.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/res/credits.csv	Wed Jul 31 23:14:27 2019 +0200
@@ -0,0 +1,181 @@
+S,"Project founder",,,
+E,,"Andrey Korotaev","unC0Rr@gmail.com","unC0Rr"
+S,"Programming",,,
+U,"Game engine",,,
+E,"Creator","Andrey Korotaev","unC0Rr@gmail.com","unC0Rr"
+E,"Many engine improvements","Derek Pomery","nemo@m8y.org","nemo"
+E,"Many engine improvements","Carlos Vives","mail@carlosvives.es",
+E,"Many engine improvements","Richard Karolyi","sheepluva@ercatec.net","sheepluva"
+E,,,"Wuzzy2@mail.ru","Wuzzy"
+E,,"Henrik Rostedt","henrik.rostedt@gmail.com",
+E,"Gamepad and Lua integration","Mario Liebisch","mario.liebisch@gmail.com",
+E,"Campaign support","Szabolcs Orbàn","szabibibi@gmail.com",
+E,"Theme customization improvements",,,"KoBeWi"
+E,"Some Pas2C and GLES2 work","Meng Xiangyun","xymengxy@gmail.com",
+E,"Video recording","Stepan Podoskin","stepik-777@mail.ru",
+E,"Other improvements","Valentin Kraevskiy",,"alfadur"
+U,"Map generation",,,
+E,"Core map generators","Andrey Korotaev","unC0Rr@gmail.com","unC0Rr"
+E,"Perlin maps and other improvements","Derek Pomery","nemo@m8y.org","nemo"
+E,"Maze maps","Henning Kühn","prg@cooco.de",
+U,"Weapons",,,
+E,"Most core weapons","Andrey Korotaev","unC0Rr@gmail.com","unC0Rr"
+E,"Air mine, rubber, others","Derek Pomery","nemo@m8y.org","nemo"
+E,"Drill rocket, ballgun, RC plane","Martin Boze","afffect@gmail.com",
+E,"Freezer","Julia Struchenko","urbertar@gmail.com",
+E,"Mine number and time game settings","David A. Cuadrado","krawek@gmail.com",
+M,,,,
+U,"Frontend / main menu",,,
+E,"Creator","Andrey Korotaev","unC0Rr@gmail.com","unC0Rr"
+E,"Many frontend improvements","Derek Pomery","nemo@m8y.org","nemo"
+E,"Many frontend improvements","Richard Karolyi","sheepluva@ercatec.net","sheepluva"
+E,"Many frontend improvements","Igor Ulyanov","disinbox@gmail.com",
+E,"Keybinds, feedback, maps and hats interfaces","Drew Gottlieb","gottlieb.drew@gmail.com",
+E,"Login dialogs, other improvements","Ondrej Skopek","skopekondrej@gmail.com",
+E,,,"Wuzzy2@mail.ru","Wuzzy"
+E,,"Martin Minarik","ttsmj@pokec.sk",
+E,,"Kristian Lehmann","email@thexception.net",
+E,,"Henrik Rostedt","henrik.rostedt@gmail.com",
+E,,"Mayur Pawashe","zorgiepoo@gmail.com",
+E,,"Valentin Kraevskiy",,"alfadur"
+U,"Missions and styles",,,
+E,"A Classic Fairytale","Szabolcs Orbàn","szabibibi@gmail.com",
+E,"A Space Adventure",,,"Master_ex"
+E,"Created Capture the Flag, Construction Mode, Control, HedgeEditor, Highlander, Racer, TechRacer, The Specialists, WxW",,,"mikade"
+E,"Training, time-trial and target practice challenges, Bazooka Battlefield, Tentacle Terror, Big Armory, bugfixes and maintenance",,"Wuzzy2@mail.ru","Wuzzy"
+E,"Some styles and missions","John Lambert","redgrinner@gmail.com","redgrinner"
+E,"Battalion",,"Anachron14@gmx.de","Anachron"
+E,"Continental supplies",,,"Vatten"
+E,"Teamwork 2",,,"Arkhnen"
+E,"Climb Home","Derek Pomery","nemo@m8y.org","nemo"
+E,"Portal Mind Challenge",,,"sphrix"
+M,,,,
+U,"Game server",,,
+E,"Creator","Andrey Korotaev","unC0Rr@gmail.com","unC0Rr"
+M,,,,
+U,"Ports",,,
+E,"macOS/iPhone port, OpenGL-ES conversion","Vittorio Giovara","vittorio.giovara@gmail.com",
+E,"Android port","Richard Deurwaarder","xeli@xelification.com","xeli"
+E,"Android netplay, portability abstraction","Simeon Maxein","smaxein@googlemail.com",
+E,"WebGL port","Meng Xiangyun","xymengxy@gmail.com",
+E,"iPhone/iPad ports","Anton Malmygin","antonc27@mail.ru","antonc27"
+S,"Graphics",,,
+U,"General",,,
+E,,"John Dum","fizzy@gmail.com",
+E,,"Joshua Frese","joshfrese@gmail.com",
+E,,"Stanko Tadić","stanko@mfhinc.net",
+E,,"Julien Koesten","julienkoesten@aol.com",
+E,,"Joshua O'Sullivan","coheedftw@hotmail.co.uk",
+E,,"Nils Lück","nils.luck.design@gmail.com",
+E,,"Guillaume Englert","genglert@hybird.org",
+E,,,"ppicondo.cvac@gmail.com","CopherNeue"
+E,,"Valentin Kraevskiy",,"alfadur"
+E,,"Carlos Vives","mail@carlosvives.es",
+U,"Themes",,,
+E,"Nature, Snow, City, Castle, Halloween, Island","John Dum","fizzy@gmail.com",
+E,"Bamboo, EarthRise, BambooPlinko","Joshua Frese","joshfrese@gmail.com",
+E,"Golf, Hoggywood, Stage",,,"RoFra"
+E,"Hoggywood",,"Wuzzy2@mail.ru","Wuzzy"
+E,"Cave, Olympics","Guillaume Englert","genglert@hybird.org",
+E,"Fruit, Cake","Randy Broda",,"Randy"
+E,"Art",,,"Zippy"
+E,"Beach",,"ppicondo.cvac@gmail.com","CopherNeue"
+E,"Beach",,,"Miguelac"
+E,"Brick",,,"AlexYeCu"
+E,"Hell","Stanko Tadić","stanko@mfhinc.net",
+E,"Jungle",,,"KoRn666"
+E,"Sheep","Julien Koesten","julienkoesten@aol.com",
+M,,,,
+U,"Maps",,,
+E,"Basketball, BasketballField, Bath, Bubbleflow, Hammock, Hedgelove, Hedgewars, Hydrant, Mushrooms, Plane, Ropes, Tree","John Dum","fizzy@gmail.com",
+E,"SB_Bones, SB_Crystal, SB_Grassy, SB_Grove, SB_Haunty, SB_Oaks, SB_Shrooms, SB_Tentacle","Chucklefish, Ltd",,
+E,"Bamboo, Blox, Cake, Cogs, EarthRise, Freeway","Joshua Frese","joshfrese@gmail.com",
+E,"Castle, PirateFlag","Stanko Tadić","stanko@mfhinc.net",
+E,"ShoppaKing, TrophyRace",,,"wolfmarc"
+E,"ShoppaKing, TrophyRace",,,"Dragonfly"
+E,"Battlefield",,,"nickstu"
+E,"CTF_Blizzard",,,"Palewolf"
+E,"Cheese",,"ppicondo.cvac@gmail.com","CopherNeue"
+E,"ClimbHome","Derek Pomery","nemo@m8y.org","nemo"
+E,"Lonely_Island","Maciej Mrozinski","mynick2@o2.pl","alzen"
+E,"Octorama",,,"jessor"
+E,"portal",,,"sphrix"
+E,"Ruler","Guillaume Englert","genglert@hybrid.org",
+E,"Sticks",,,"dctPL"
+M,,,,
+U,"Forts",,,
+E,"EvilChicken",,,"Dragonfly"
+E,"Lonely_Island","Maciej Mrozinski","mynick2@o2.pl","alzen"
+E,"Olympic","Guillaume Englert","genglert@hybird.org",
+E,"Olympic",,"Wuzzy2@mail.ru","Wuzzy"
+E,"Tank","Carlos Vives","mail@carlosvives.es",
+E,"Snail","John Dum","fizzy@gmail.com",
+E,"Snail",,"Wuzzy2@mail.ru","Wuzzy"
+E,"SteelTower","Randy Broda",,"Randy"
+M,,,,
+U,"Hats, graves, other",,,
+E,"See CREDITS text file",,,
+S,"Sounds",,,
+E,"Hedgehogs voice","Stephen Alexander","ArmagonNo1@gmail.com","Armagon"
+E,"Default_pl, Russian_pl voices",,"mtg90pl@gmail.com","mtg90pl"
+E,,"John Dum","fizzy@gmail.com",
+E,,"Jonatan Nilsson","jonatanfan@gmail.com",
+E,,"Daniel Martin","elhombresinremedio@gmail.com","HSR"
+E,"Various authors from www.freesound.org (see CREDITS text file)",,
+S,"Music",,,
+E,"City, Rock, others","Daniel Martin","elhombresinremedio@gmail.com","HSR"
+E,"Compost",,,"HG"
+E,"EarthRise, oriental, Pirate, snow","Jonatan Nilsson","jonatanfan@gmail.com",
+E,"Fruit, Jungle","Valentin Kraevskiy",,"alfadur"
+E,"Nature","John Dum","fizzy@gmail.com",
+E,"olympics_sd",,,"yd &lt;http://opengameart.org/users/yd&gt;"
+E,"sdmusic (Hitman [sheepluva edit])","Kevin MacLeod",,
+M,,,,
+S,"Translations",,,
+E,"Brazilian Portuguese","Romulo Fernandes Machado","abra185@gmail.com",
+E,"Bulgarian","Svetoslav Stefanov",,
+E,"Czech","Petr Řezáček","rezacek@gmail.com",
+E,"Chinese","Jie Luo","lililjlj@gmail.com",
+E,"Chinese",,"yuenfu.chiu@gmail.com","yuenfu"
+E,"Finnish","Nina Kuisma","ninnnu@gmail.com",
+E,"Finnish","Janne Uusitupa",,
+E,"French","Antoine Turmel","geekshadow@gmail.com",
+E,"French","Clement Woitrain","sphrixclement@gmail.com",
+E,"French",,,"Matisumi"
+E,"French",,,"Case_Of"
+E,"German","Peter Hüwe","PeterHuewe@gmx.de",
+E,"German","Mario Liebisch","mario.liebisch@gmail.com",
+E,"German","Richard Karolyi","sheepluva@ercatec.net","sheepluva"
+E,"German",,"Wuzzy2@mail.ru","Wuzzy"
+E,"Greek",,"talos_kriti@yahoo.gr",
+E,"Italian","Luca Bonora","bonora.luca@gmail.com",
+E,"Italian","Marco Bresciani","m.bresciani@email.it",
+E,"Italian","Gianfranco Costamagna","costamagnagianfranco@yahoo.it",
+E,"Italian",,"enricobe@hotmail.com","Enrico"
+E,"Japanese","ADAM Etienne","etienne.adam@gmail.com",
+E,"Japanese","Marco Bresciani","m.bresciani@email.it",
+E,"Korean","Anthony Bellew","anthonyreflected@gmail.com",
+E,"Lithuanian","Lukas Urbonas","lukasu08@gmail.com",
+E,"Polish","Maciej Mroziński","mynick2@o2.pl","alzen"
+E,"Polish","Wojciech Latkowski","magik17l@gmail.com",
+E,"Polish","Piotr Mitana",,
+E,"Polish","Maciej Górny",,
+E,"Polish",,,"KoBeWi"
+E,"Portuguese","Fábio Canário","inufabie@gmail.com",
+E,"Russian","Andrey Korotaev","unC0Rr@gmail.com","unC0Rr"
+E,"Russian","Vitaly Novichkov","admin@wohlnet.ru",
+E,"Russian","Anton Malmygin","antonc27@mail.ru","antonc27"
+E,"Russian","Grigory Ustinov","grenka@altlinux.org","grenka"
+E,"Scottish Gaelic",,,"GunChleoc"
+E,"Slovak","Jose Riha",,
+E,"Spanish","Carlos Vives","mail@carlosvives.es",
+E,"Swedish","Niklas Grahn","raewolusjoon@yaoo.com",
+E,"Swedish","Henrik Rostedt","henrik.rostedt@gmail.com",
+E,"Ukrainian","Eugene V. Lyubimkin","jackyf.devel@gmail.com",
+E,"Ukrainian","Igor Paliychuk","mansonigor@gmail.com",
+E,"Ukrainian","Eugene Sakara","eresid@gmail.com",
+S,"Special thanks",,,
+E,,"Aleksey Andreev","blaknayabr@gmail.com",,
+E,,"Aleksander Rudalev","alexv@pomorsu.ru",,
+E,,"Natasha Korotaeva","layout@pisem.net",,
+E,,"Adam Higerd",,"ahigerd"
--- a/QTfrontend/res/css/april1.css	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/res/css/april1.css	Wed Jul 31 23:14:27 2019 +0200
@@ -1,30 +1,35 @@
 /******************************************************************************
  *
- * CSS-like definition of Qt frontend appearance
+ * CSS definition of Qt frontend appearance: April 1 style.
  *
  ******************************************************************************
  *
- * see http://doc.qt.nokia.com/4.5/stylesheet.html
+ * see https://doc.qt.io/qt-5/style-reference.html
  *
  ******************************************************************************
  *
  * This file can be stored at different locations, but it will be read only
  * once, based on first file found in this order:
  *
- *    <userdir>/Data/css/qt.css
- *    <datadir>/css/qt.css
- *    <internal default style-sheet> (:/res/css/qt.css)
+ *    <userdir>/Data/css/april1.css
+ *    <datadir>/css/april1.css
+ *    <internal default style-sheet> (:/res/css/april1.css)
+ *
+ *****************************************************************************
+ *
+ * This file is based off qt.css with minimal changes. The altered parts are
+ * marked with “CUSTOM”.
  *
  *****************************************************************************/
 
-#infoButton 
+#infoButton
 {
 border: transparent;
-background: transparent; 
-width: 800px;
-min-width: 800px;
-qproperty-icon: url(":/res/TomatowarsTitle.png");
-qproperty-iconSize: 800px 165px;
+background: transparent;
+width: 800px; /* CUSTOM */
+min-width: 800px; /* CUSTOM */
+qproperty-icon: url(":/res/TomatowarsTitle.png"); /* CUSTOM */
+qproperty-iconSize: 800px 165px; /* CUSTOM */
 }
 HWForm,QDialog {
 background-image: url(":/res/Background.png");
@@ -106,10 +111,6 @@
 background-color: #150A61;
 }
 
-QToolButton:pressed {
-background-color: #100744;
-}
-
 QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser,
 QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
 border-radius: 10px;
@@ -175,6 +176,11 @@
 
 QPushButton:pressed, QToolButton:pressed {
 border-color: white;
+background-color: #344b1e;
+color: white;
+}
+TeamShowWidget QPushButton:pressed {
+background-color: #362c7a;
 }
 
 QPushButton:focus {
@@ -197,11 +203,12 @@
 border-bottom-width: 0px;
 border-color: #001d10;
 border-style: solid;
-background-color: #00351d;
+background-color: #005F35;
 padding: 4px;
 }
 QHeaderView::section:pressed {
-background-color: #00250d;
+background-color: #005A33;
+border-color: #FFFFFF;
 }
 QHeaderView::up-arrow {
 image: url(":/res/sort_up.png");
@@ -231,6 +238,7 @@
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
 QSpinBox::up-arrow {
@@ -251,8 +259,13 @@
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
+QSpinBox, QLineEdit {
+padding: 3px;
+min-height: 18px;
+}
 QComboBox {
 border-radius: 10px;
 padding: 3px;
@@ -405,10 +418,80 @@
 border-color: #F6CB1C;
 }
 
-#hatList QScrollBar, #themeList QScrollBar {
-background-color: #130F2A;
-border-top-right-radius: 10px;
-border-bottom-right-radius: 10px;
+QScrollBar:vertical {
+border: none;
+color: #FFD902;
+background: #00321c;
+width: 15px;
+margin: 17px 0 17px 0;
+}
+QScrollBar:horizontal {
+border: none;
+color: #FFD902;
+background: #00321c;
+height: 15px;
+margin: 0 17px 0 17px;
+}
+QScrollBar::handle:vertical {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-height: 20px;
+}
+QScrollBar::handle:horizontal {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-width: 20px;
+}
+QScrollBar::handle:pressed {
+background: #005a33;
+border-color: #FFFFFF;
+}
+QScrollBar::add-line, QScrollBar::sub-line {
+border: 1px solid #005F35;
+border-radius: 2px;
+background: #00321c;
+subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical {
+height: 15px;
+subcontrol-position: bottom;
+}
+QScrollBar::sub-line:vertical {
+height: 15px;
+subcontrol-position: top;
+}
+QScrollBar::add-line:horizontal {
+width: 15px;
+subcontrol-position: right;
+}
+QScrollBar::sub-line:horizontal {
+width: 15px;
+subcontrol-position: left;
+}
+QScrollBar::add-line:pressed, QScrollBar::sub-line:pressed {
+background: #005a33;
+border-color: #FFFFFF;
+}
+
+QScrollBar::up-arrow {
+background-image: url(":/res/scroll_up.png");
+}
+QScrollBar::down-arrow {
+background-image: url(":/res/scroll_down.png");
+}
+QScrollBar::left-arrow {
+background-image: url(":/res/scroll_left.png");
+}
+QScrollBar::right-arrow {
+background-image: url(":/res/scroll_right.png");
+}
+QScrollBar::add-page, QScrollBar::sub-page {
+background: #00190F;
+}
+QScrollBar::add-page:pressed, QScrollBar::sub-page:pressed {
+background: #008751;
 }
 
 #hatList, #themeList {
@@ -437,7 +520,7 @@
 background-color: #150A61;
 }
 
-QDialogButtonBox QPushButton {
+.QPushButton, .QPushButtonWithSound {
 padding: 3px 5px;
 }
 
@@ -453,3 +536,36 @@
 PageMultiplayer TeamSelWidget {
 min-height: 500px;
 }
+
+QProgressBar {
+border: 3px solid #FFCC00;
+background-color: #150A61;
+border-radius: 5px;
+text-align: center;
+color: #FFFFFF;
+}
+QProgressBar::chunk {
+background-color: #FF9B00;
+margin: 2px;
+}
+#keyBinderScrollArea {
+background: #130F2A;
+}
+#chatContainer {
+border-width: 0px;
+background-color: #ffcc00;
+border-radius: 10px;
+}
+#chatText {
+background-color: rgb(23, 11, 54);
+border-width: 0px;
+}
+#trainingList {
+border-style: none;
+padding-top: 6px;
+}
+#trainingList::item
+{
+padding-top: 2px;
+padding-bottom: 2px;
+}
--- a/QTfrontend/res/css/birthday.css	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/res/css/birthday.css	Wed Jul 31 23:14:27 2019 +0200
@@ -1,10 +1,10 @@
 /******************************************************************************
  *
- * CSS-like definition of Qt frontend appearance
+ * CSS-like definition of Qt frontend appearance: Hedgewars' Birthday.
  *
  ******************************************************************************
  *
- * see http://doc.qt.nokia.com/4.5/stylesheet.html
+ * see https://doc.qt.io/qt-5/style-reference.html
  *
  ******************************************************************************
  *
@@ -12,17 +12,35 @@
  * once, based on first file found in this order:
  *
  *    <userdir>/Data/css/birthday.css
- *    <datadir>/css/birthday.css
+ *    <datadir>/css/brithday.css
  *    <internal default style-sheet> (:/res/css/birthday.css)
  *
+ *****************************************************************************
+ *
+ * This file is based off qt.css with changes. The altered parts are
+ * marked with “CUSTOM”.
+ *
  *****************************************************************************/
 
-HWForm,QDialog {
-background-image: url(":/res/BackgroundBirthday.png");
+#infoButton
+{
+border: transparent;
+background: transparent;
+}
+HWForm {
+background-image: url(":/res/BackgroundBirthday.png"); /* CUSTOM */
 background-position: bottom center;
 background-repeat: repeat-x;
-background-color: #100308;
+background-color: #100308; /* CUSTOM */
 }
+QDialog{ /* CUSTOM */
+background-color: #100308; /* CUSTOM */
+} /* CUSTOM: This makes yellow text readable (because yellow text vs white moon) */
+#campaignInfo, #trainingInfo{ /* CUSTOM */
+background-color: rgba(20, 20, 20, 70%); /* CUSTOM */
+border-radius: 16px;
+padding: 6px;
+} /* CUSTOM */
 
 * {
 color: #ffcc00;
@@ -32,16 +50,36 @@
 
 a { color:#c8c8ff; }
 
-QLineEdit, QListWidget, QListView, QTableView, QTextBrowser, QSpinBox, QComboBox,
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser, QSpinBox, QComboBox,
 QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item, #labelLikeLineEdit {
-background-color: rgba(20, 20, 20, 70%);
+background-color: rgba(20, 20, 20, 70%); /* CUSTOM */
+}
+
+VertScrArea, QGraphicsView {
+border-style: solid; border-width: 2px; border-color: #cca300; border-radius: 3px;
+}
+#gameStatsView {
+border-color: #332816;
+}
+
+QSplitter::handle {
+background-image: url(":/res/splitter.png");
+background-clip: content;
+}
+QSplitter::handle:horizontal {
+width: 7px;
+background-repeat: repeat-y;
+}
+QSplitter::handle:vertical {
+height: 7px;
+background-repeat: repeat-x;
 }
 
 QComboBox::separator {
 border: solid; border-width: 3px; border-color: #ffcc00;
 }
 
-QPushButton, QListWidget, QListView, QTableView, QLineEdit, QHeaderView,
+QPushButton, QListWidget, QListView, QTableView, QTableWidget, QLineEdit,
 QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget,
@@ -56,33 +94,51 @@
 border-color: yellow;
 }
 
+TeamShowWidget QPushButton {
+icon-size: 48px;
+text-align: left;
+background-color: #0d0d0d; /* CUSTOM */
+color: orange;
+font: bold;
+border-width: 2px;
+margin: 6px 0px 6px 0px;
+}
+TeamShowWidget QPushButton:disabled {
+color: #a0a0a0;
+}
+
 QToolButton {
-background-color: #11084A;
+background-color: #080808; /* CUSTOM */
 }
 
 QToolButton:hover {
 background-color: #150A61;
 }
 
-QToolButton:pressed {
-background-color: #100744;
-}
-
-QLineEdit, QListWidget, QListView, QTableView, QTextBrowser,
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser,
 QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
 border-radius: 10px;
 }
 
 #mapPreview {
-background-color: #0d0544;
+background-color: #0d0d0d; /* CUSTOM */
+}
+#mapPreview:disabled{
+border-color: #a0a0a0;
+background-color: #0d0d0d; /* CUSTOM */
+color: #ffffff;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QListView, QTableView,
-QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
+QTableWidget, QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, #gameStackContainer, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit,
+#mapName {
 font: bold 13px;
 }
+.QLabel{
+background-color: transparent;
+}
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
 background-position: bottom center;
 background-repeat: repeat-x;
@@ -94,6 +150,7 @@
 border-radius: 16px;
 background-color: rgba(20, 20, 20, 70%);
 padding: 6px;
+margin-top: 4px;
 }
 /*  Experimenting with PaintOnScreen and border-radius on IconedGroupBox children didn't work out well
 IconedGroupBox QComboBox, IconedGroupBox QPushButton, IconedGroupBox QLineEdit,
@@ -110,10 +167,6 @@
 border-top-left-radius: 0px;
 }
 
-QLineEdit:disabled, QSpinBox:disabled {
-border-color: gray;
-}
-
 GameCFGWidget {
 border: none;
 }
@@ -122,11 +175,16 @@
 border-radius: 8px;
 background-origin: margin;
 background-position: top left;
-background-color: rgba(18, 42, 5, 70%);
+background-color: rgba(20, 20, 20, 70%); /* CUSTOM */
 }
 
 QPushButton:pressed, QToolButton:pressed {
 border-color: white;
+background-color: rgba(40, 40, 40, 100%); /* CUSTOM */
+color: white;
+}
+TeamShowWidget QPushButton:pressed {
+background-color: #202020; /* CUSTOM */
 }
 
 QPushButton:focus {
@@ -134,12 +192,36 @@
 }
 
 QHeaderView {
-border-radius: 0;
-border-width: 0;
+background-color: #000000;
+border: solid;
 border-bottom-width: 3px;
-background-color: #00351d;
+border-top-width: 0px;
+border-left-width: 0px;
+border-right-width: 0px;
+border-color: #ffcc00;
 }
-QTableView {
+QHeaderView::section {
+border-left-width: 1px;
+border-right-width: 1px;
+border-top-width: 0;
+border-bottom-width: 0px;
+border-color: #5A5A5A;
+border-style: solid;
+background-color: #000000;
+padding: 4px;
+}
+QHeaderView::section:pressed {
+background-color: #333333;
+border-color: #FFFFFF;
+}
+QHeaderView::up-arrow {
+image: url(":/res/sort_up.png");
+}
+QHeaderView::down-arrow{
+image: url(":/res/sort_down.png");
+}
+
+QTableView, QTableWidget {
 alternate-background-color: #2f213a;
 gridline-color: transparent;
 }
@@ -149,7 +231,7 @@
 border-top-left-radius: 6px;
 border-top-right-radius: 6px;
 padding: 3px;
-background-color: #00351d;
+background-color: #000000; /* CUSTOM */
 color: #ffcc00;
 }
 QTabBar::tab:selected {
@@ -160,22 +242,34 @@
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
 QSpinBox::up-arrow {
 image: url(":/res/spin_up.png");
 }
+QSpinBox::up-arrow:disabled {
+image: url(":/res/spin_up_disabled.png");
+}
 
 QSpinBox::down-arrow {
 image: url(":/res/spin_down.png");
 }
+QSpinBox::down-arrow:disabled {
+image: url(":/res/spin_down_disabled.png");
+}
 
 QSpinBox::down-button {
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
+QSpinBox, QLineEdit {
+padding: 3px;
+min-height: 18px;
+}
 QComboBox {
 border-radius: 10px;
 padding: 3px;
@@ -191,6 +285,9 @@
 QComboBox::down-arrow {
 image: url(":/res/dropdown.png");
 }
+QComboBox::down-arrow:disabled {
+image: url(":/res/dropdown_disabled.png");
+}
 
 VertScrArea {
 background-position: bottom center;
@@ -207,15 +304,45 @@
 subcontrol-position: top left;
 text-align: left;
 left: 15px;
-top: -4px;
 }
 
 QCheckBox::indicator:checked{
 image: url(":/res/checked.png");
 }
+QCheckBox::indicator:checked:hover{
+image: url(":/res/checkedHover.png");
+}
+QCheckBox::indicator:checked:pressed{
+image: url(":/res/checkedPressed.png");
+}
 QCheckBox::indicator:unchecked{
 image: url(":/res/unchecked.png");
 }
+QCheckBox::indicator:unchecked:hover{
+image: url(":/res/uncheckedHover.png");
+}
+QCheckBox::indicator:unchecked:pressed{
+image: url(":/res/uncheckedPressed.png");
+}
+
+QRadioButton::indicator:checked{
+image: url(":/res/radioButtonChecked.png");
+}
+QRadioButton::indicator:checked:hover{
+image: url(":/res/radioButtonCheckedHover.png");
+}
+QRadioButton::indicator:checked:pressed{
+image: url(":/res/radioButtonCheckedPressed.png");
+}
+QRadioButton::indicator:unchecked{
+image: url(":/res/radioButtonUnchecked.png");
+}
+QRadioButton::indicator:unchecked:hover{
+image: url(":/res/radioButtonUncheckedHover.png");
+}
+QRadioButton::indicator:unchecked:pressed{
+image: url(":/res/radioButtonUncheckedPressed.png");
+}
 
 .QWidget{
 background: transparent;
@@ -250,12 +377,17 @@
 }
 
 QToolTip{
-background-color: #0d0544;
+background-color: #0d0d0d; /* CUSTOM */
 border: 1px solid #ffcc00;
 }
 
 :disabled{
 color: #a0a0a0;
+border-color: #a0a0a0;
+}
+QListWidget:item:selected:disabled, QListView:item:selected:disabled{
+color: rgba(13, 5, 68, 70%);
+background-color: #a0a0a0;
 }
 SquareLabel, ItemNum {
 background-color: #000000;
@@ -266,6 +398,9 @@
 margin: 2px 0px;
 background-color: #ffcc00;
 }
+QSlider::groove::horizontal:disabled {
+background-color: #a0a0a0;
+}
 
 QSlider::handle::horizontal {
 border: 0px;
@@ -275,6 +410,9 @@
 height: 6px;
 border-radius: 3px;
 }
+QSlider::handle::horizontal:disabled {
+background-color: #a0a0a0;
+}
 
 HatButton, ThemeButton {
 text-align: left;
@@ -284,10 +422,80 @@
 border-color: #F6CB1C;
 }
 
-#hatList QScrollBar, #themeList QScrollBar {
-background-color: #130F2A;
-border-top-right-radius: 10px;
-border-bottom-right-radius: 10px;
+QScrollBar:vertical {
+border: none;
+color: #FFD902;
+background: black; /*CUSTOM */
+width: 15px;
+margin: 17px 0 17px 0;
+}
+QScrollBar:horizontal {
+border: none;
+color: #FFD902;
+background: black; /* CUSTOM */
+height: 15px;
+margin: 0 17px 0 17px;
+}
+QScrollBar::handle:vertical {
+background: #3d1b3f; /* CUSTOM */
+border: 1px solid #b465b8; /* CUSTOM */
+border-radius: 2px;
+min-height: 20px;
+}
+QScrollBar::handle:horizontal {
+background: #3d1b3f; /* CUSTOM */
+border: 1px solid #b465b8; /* CUSTOM */
+border-radius: 2px;
+min-width: 20px;
+}
+QScrollBar::handle:pressed {
+background: #622b66; /* CUSTOM */
+border-color: #FFFFFF;
+}
+QScrollBar::add-line, QScrollBar::sub-line {
+border: 1px solid #b465b8; /* CUSTOM */
+border-radius: 2px;
+background: #3d1b3f; /* CUSTOM */
+subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical {
+height: 15px;
+subcontrol-position: bottom;
+}
+QScrollBar::sub-line:vertical {
+height: 15px;
+subcontrol-position: top;
+}
+QScrollBar::add-line:horizontal {
+width: 15px;
+subcontrol-position: right;
+}
+QScrollBar::sub-line:horizontal {
+width: 15px;
+subcontrol-position: left;
+}
+QScrollBar::add-line:pressed, QScrollBar::sub-line:pressed {
+background: #622b66; /* CUSTOM */
+border-color: #FFFFFF;
+}
+
+QScrollBar::up-arrow {
+background-image: url(":/res/scroll_up.png");
+}
+QScrollBar::down-arrow {
+background-image: url(":/res/scroll_down.png");
+}
+QScrollBar::left-arrow {
+background-image: url(":/res/scroll_left.png");
+}
+QScrollBar::right-arrow {
+background-image: url(":/res/scroll_right.png");
+}
+QScrollBar::add-page, QScrollBar::sub-page {
+background: black; /* CUSTOM */
+}
+QScrollBar::add-page:pressed, QScrollBar::sub-page:pressed {
+background: #808080; /* CUSTOM */
 }
 
 #hatList, #themeList {
@@ -316,7 +524,7 @@
 background-color: #150A61;
 }
 
-QDialogButtonBox QPushButton {
+.QPushButton, .QPushButtonWithSound {
 padding: 3px 5px;
 }
 
@@ -332,3 +540,36 @@
 PageMultiplayer TeamSelWidget {
 min-height: 500px;
 }
+
+QProgressBar {
+border: 3px solid #FFCC00;
+background-color: black; /* CUSTOM */
+border-radius: 5px;
+text-align: center;
+color: #FFFFFF;
+}
+QProgressBar::chunk {
+background-color: #FF9B00;
+margin: 2px;
+}
+#keyBinderScrollArea {
+background: #100308; /* CUSTOM */
+}
+#chatContainer {
+border-width: 0px;
+background-color: #ffcc00;
+border-radius: 10px;
+}
+#chatText {
+background-color: rgb(23, 11, 54);
+border-width: 0px;
+}
+#trainingList {
+border-style: none;
+padding-top: 6px;
+}
+#trainingList::item
+{
+padding-top: 2px;
+padding-bottom: 2px;
+}
--- a/QTfrontend/res/css/christmas.css	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/res/css/christmas.css	Wed Jul 31 23:14:27 2019 +0200
@@ -1,10 +1,10 @@
 /******************************************************************************
  *
- * CSS-like definition of Qt frontend appearance
+ * CSS definition of Qt frontend appearance: Christmas style.
  *
  ******************************************************************************
  *
- * see http://doc.qt.nokia.com/4.5/stylesheet.html
+ * see https://doc.qt.io/qt-5/style-reference.html
  *
  ******************************************************************************
  *
@@ -15,14 +15,27 @@
  *    <datadir>/css/christmas.css
  *    <internal default style-sheet> (:/res/css/christmas.css)
  *
+ *****************************************************************************
+ *
+ * This file is based off qt.css with minimal changes. The altered parts are
+ * marked with “CUSTOM”.
+ *
  *****************************************************************************/
 
-HWForm,QDialog {
-background-image: url(":/res/BackgroundChristmas.png");
+#infoButton
+{
+border: transparent;
+background: transparent;
+}
+HWForm {
+background-image: url(":/res/BackgroundChristmas.png"); /* CUSTOM */
 background-position: bottom center;
 background-repeat: repeat-x;
-background-color: #0c0f28;
+background-color: #0c0f28; /* CUSTOM */
 }
+QDialog{ /* CUSTOM */
+background-color: #0c0f28; /* CUSTOM */
+} /* CUSTOM */
 
 * {
 color: #ffcc00;
@@ -32,16 +45,36 @@
 
 a { color:#c8c8ff; }
 
-QLineEdit, QListWidget, QListView, QTableView, QTextBrowser, QSpinBox, QComboBox,
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser, QSpinBox, QComboBox,
 QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item, #labelLikeLineEdit {
 background-color: rgba(13, 5, 68, 70%);
 }
 
+VertScrArea, QGraphicsView {
+border-style: solid; border-width: 2px; border-color: #cca300; border-radius: 3px;
+}
+#gameStatsView {
+border-color: #332816;
+}
+
+QSplitter::handle {
+background-image: url(":/res/splitter.png");
+background-clip: content;
+}
+QSplitter::handle:horizontal {
+width: 7px;
+background-repeat: repeat-y;
+}
+QSplitter::handle:vertical {
+height: 7px;
+background-repeat: repeat-x;
+}
+
 QComboBox::separator {
 border: solid; border-width: 3px; border-color: #ffcc00;
 }
 
-QPushButton, QListWidget, QListView, QTableView, QLineEdit, QHeaderView,
+QPushButton, QListWidget, QListView, QTableView, QTableWidget, QLineEdit,
 QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget,
@@ -56,6 +89,19 @@
 border-color: yellow;
 }
 
+TeamShowWidget QPushButton {
+icon-size: 48px;
+text-align: left;
+background-color: #0d0544;
+color: orange;
+font: bold;
+border-width: 2px;
+margin: 6px 0px 6px 0px;
+}
+TeamShowWidget QPushButton:disabled {
+color: #a0a0a0;
+}
+
 QToolButton {
 background-color: #11084A;
 }
@@ -64,11 +110,7 @@
 background-color: #150A61;
 }
 
-QToolButton:pressed {
-background-color: #100744;
-}
-
-QLineEdit, QListWidget, QListView, QTableView, QTextBrowser,
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser,
 QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
 border-radius: 10px;
 }
@@ -76,13 +118,22 @@
 #mapPreview {
 background-color: #0d0544;
 }
+#mapPreview:disabled{
+border-color: #a0a0a0;
+background-color: #0d0544;
+color: #ffffff;
+}
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QListView, QTableView,
-QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
+QTableWidget, QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, #gameStackContainer, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit,
+#mapName {
 font: bold 13px;
 }
+.QLabel{
+background-color: transparent;
+}
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
 background-position: bottom center;
 background-repeat: repeat-x;
@@ -94,6 +145,7 @@
 border-radius: 16px;
 background-color: rgba(13, 5, 68, 70%);
 padding: 6px;
+margin-top: 4px;
 }
 /*  Experimenting with PaintOnScreen and border-radius on IconedGroupBox children didn't work out well
 IconedGroupBox QComboBox, IconedGroupBox QPushButton, IconedGroupBox QLineEdit,
@@ -110,10 +162,6 @@
 border-top-left-radius: 0px;
 }
 
-QLineEdit:disabled, QSpinBox:disabled {
-border-color: gray;
-}
-
 GameCFGWidget {
 border: none;
 }
@@ -127,6 +175,11 @@
 
 QPushButton:pressed, QToolButton:pressed {
 border-color: white;
+background-color: #344b1e;
+color: white;
+}
+TeamShowWidget QPushButton:pressed {
+background-color: #362c7a;
 }
 
 QPushButton:focus {
@@ -134,12 +187,36 @@
 }
 
 QHeaderView {
-border-radius: 0;
-border-width: 0;
+background-color: #00351d;
+border: solid;
 border-bottom-width: 3px;
-background-color: #00351d;
+border-top-width: 0px;
+border-left-width: 0px;
+border-right-width: 0px;
+border-color: #ffcc00;
 }
-QTableView {
+QHeaderView::section {
+border-left-width: 1px;
+border-right-width: 1px;
+border-top-width: 0;
+border-bottom-width: 0px;
+border-color: #001d10;
+border-style: solid;
+background-color: #005F35;
+padding: 4px;
+}
+QHeaderView::section:pressed {
+background-color: #005A33;
+border-color: #FFFFFF;
+}
+QHeaderView::up-arrow {
+image: url(":/res/sort_up.png");
+}
+QHeaderView::down-arrow{
+image: url(":/res/sort_down.png");
+}
+
+QTableView, QTableWidget {
 alternate-background-color: #2f213a;
 gridline-color: transparent;
 }
@@ -160,22 +237,34 @@
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
 QSpinBox::up-arrow {
 image: url(":/res/spin_up.png");
 }
+QSpinBox::up-arrow:disabled {
+image: url(":/res/spin_up_disabled.png");
+}
 
 QSpinBox::down-arrow {
 image: url(":/res/spin_down.png");
 }
+QSpinBox::down-arrow:disabled {
+image: url(":/res/spin_down_disabled.png");
+}
 
 QSpinBox::down-button {
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
+QSpinBox, QLineEdit {
+padding: 3px;
+min-height: 18px;
+}
 QComboBox {
 border-radius: 10px;
 padding: 3px;
@@ -191,6 +280,9 @@
 QComboBox::down-arrow {
 image: url(":/res/dropdown.png");
 }
+QComboBox::down-arrow:disabled {
+image: url(":/res/dropdown_disabled.png");
+}
 
 VertScrArea {
 background-position: bottom center;
@@ -207,15 +299,45 @@
 subcontrol-position: top left;
 text-align: left;
 left: 15px;
-top: -4px;
 }
 
 QCheckBox::indicator:checked{
 image: url(":/res/checked.png");
 }
+QCheckBox::indicator:checked:hover{
+image: url(":/res/checkedHover.png");
+}
+QCheckBox::indicator:checked:pressed{
+image: url(":/res/checkedPressed.png");
+}
 QCheckBox::indicator:unchecked{
 image: url(":/res/unchecked.png");
 }
+QCheckBox::indicator:unchecked:hover{
+image: url(":/res/uncheckedHover.png");
+}
+QCheckBox::indicator:unchecked:pressed{
+image: url(":/res/uncheckedPressed.png");
+}
+
+QRadioButton::indicator:checked{
+image: url(":/res/radioButtonChecked.png");
+}
+QRadioButton::indicator:checked:hover{
+image: url(":/res/radioButtonCheckedHover.png");
+}
+QRadioButton::indicator:checked:pressed{
+image: url(":/res/radioButtonCheckedPressed.png");
+}
+QRadioButton::indicator:unchecked{
+image: url(":/res/radioButtonUnchecked.png");
+}
+QRadioButton::indicator:unchecked:hover{
+image: url(":/res/radioButtonUncheckedHover.png");
+}
+QRadioButton::indicator:unchecked:pressed{
+image: url(":/res/radioButtonUncheckedPressed.png");
+}
 
 .QWidget{
 background: transparent;
@@ -256,6 +378,11 @@
 
 :disabled{
 color: #a0a0a0;
+border-color: #a0a0a0;
+}
+QListWidget:item:selected:disabled, QListView:item:selected:disabled{
+color: rgba(13, 5, 68, 70%);
+background-color: #a0a0a0;
 }
 SquareLabel, ItemNum {
 background-color: #000000;
@@ -266,6 +393,9 @@
 margin: 2px 0px;
 background-color: #ffcc00;
 }
+QSlider::groove::horizontal:disabled {
+background-color: #a0a0a0;
+}
 
 QSlider::handle::horizontal {
 border: 0px;
@@ -275,6 +405,9 @@
 height: 6px;
 border-radius: 3px;
 }
+QSlider::handle::horizontal:disabled {
+background-color: #a0a0a0;
+}
 
 HatButton, ThemeButton {
 text-align: left;
@@ -284,10 +417,80 @@
 border-color: #F6CB1C;
 }
 
-#hatList QScrollBar, #themeList QScrollBar {
-background-color: #130F2A;
-border-top-right-radius: 10px;
-border-bottom-right-radius: 10px;
+QScrollBar:vertical {
+border: none;
+color: #FFD902;
+background: #00321c;
+width: 15px;
+margin: 17px 0 17px 0;
+}
+QScrollBar:horizontal {
+border: none;
+color: #FFD902;
+background: #00321c;
+height: 15px;
+margin: 0 17px 0 17px;
+}
+QScrollBar::handle:vertical {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-height: 20px;
+}
+QScrollBar::handle:horizontal {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-width: 20px;
+}
+QScrollBar::handle:pressed {
+background: #005a33;
+border-color: #FFFFFF;
+}
+QScrollBar::add-line, QScrollBar::sub-line {
+border: 1px solid #005F35;
+border-radius: 2px;
+background: #00321c;
+subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical {
+height: 15px;
+subcontrol-position: bottom;
+}
+QScrollBar::sub-line:vertical {
+height: 15px;
+subcontrol-position: top;
+}
+QScrollBar::add-line:horizontal {
+width: 15px;
+subcontrol-position: right;
+}
+QScrollBar::sub-line:horizontal {
+width: 15px;
+subcontrol-position: left;
+}
+QScrollBar::add-line:pressed, QScrollBar::sub-line:pressed {
+background: #005a33;
+border-color: #FFFFFF;
+}
+
+QScrollBar::up-arrow {
+background-image: url(":/res/scroll_up.png");
+}
+QScrollBar::down-arrow {
+background-image: url(":/res/scroll_down.png");
+}
+QScrollBar::left-arrow {
+background-image: url(":/res/scroll_left.png");
+}
+QScrollBar::right-arrow {
+background-image: url(":/res/scroll_right.png");
+}
+QScrollBar::add-page, QScrollBar::sub-page {
+background: #00190F;
+}
+QScrollBar::add-page:pressed, QScrollBar::sub-page:pressed {
+background: #008751;
 }
 
 #hatList, #themeList {
@@ -316,7 +519,7 @@
 background-color: #150A61;
 }
 
-QDialogButtonBox QPushButton {
+.QPushButton, .QPushButtonWithSound {
 padding: 3px 5px;
 }
 
@@ -332,3 +535,36 @@
 PageMultiplayer TeamSelWidget {
 min-height: 500px;
 }
+
+QProgressBar {
+border: 3px solid #FFCC00;
+background-color: #150A61;
+border-radius: 5px;
+text-align: center;
+color: #FFFFFF;
+}
+QProgressBar::chunk {
+background-color: #FF9B00;
+margin: 2px;
+}
+#keyBinderScrollArea {
+background: #130F2A;
+}
+#chatContainer {
+border-width: 0px;
+background-color: #ffcc00;
+border-radius: 10px;
+}
+#chatText {
+background-color: rgb(23, 11, 54);
+border-width: 0px;
+}
+#trainingList {
+border-style: none;
+padding-top: 6px;
+}
+#trainingList::item
+{
+padding-top: 2px;
+padding-bottom: 2px;
+}
--- a/QTfrontend/res/css/easter.css	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/res/css/easter.css	Wed Jul 31 23:14:27 2019 +0200
@@ -1,10 +1,10 @@
 /******************************************************************************
  *
- * CSS-like definition of Qt frontend appearance
+ * CSS definition of Qt frontend appearance. Easter mode.
  *
  ******************************************************************************
  *
- * see http://doc.qt.nokia.com/4.5/stylesheet.html
+ * see https://doc.qt.io/qt-5/style-reference.html
  *
  ******************************************************************************
  *
@@ -15,10 +15,20 @@
  *    <datadir>/css/easter.css
  *    <internal default style-sheet> (:/res/css/easter.css)
  *
- *****************************************************************************/
+ ******************************************************************************
+ *
+ * This file is based off qt.css with minimal changes. The altered parts are
+ * marked with “CUSTOM”.
+ *
+ ******************************************************************************/
 
+#infoButton
+{
+border: transparent;
+background: transparent;
+}
 HWForm,QDialog {
-background-image: url(":/res/BackgroundEaster.png");
+background-image: url(":/res/BackgroundEaster.png"); /* CUSTOM */
 background-position: bottom center;
 background-repeat: repeat-x;
 background-color: #141250;
@@ -32,20 +42,40 @@
 
 a { color:#c8c8ff; }
 
-QLineEdit, QListWidget, QListView, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser, QSpinBox, QComboBox,
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item, #labelLikeLineEdit {
 background-color: rgba(13, 5, 68, 70%);
 }
 
+VertScrArea, QGraphicsView {
+border-style: solid; border-width: 2px; border-color: #cca300; border-radius: 3px;
+}
+#gameStatsView {
+border-color: #332816;
+}
+
+QSplitter::handle {
+background-image: url(":/res/splitter.png");
+background-clip: content;
+}
+QSplitter::handle:horizontal {
+width: 7px;
+background-repeat: repeat-y;
+}
+QSplitter::handle:vertical {
+height: 7px;
+background-repeat: repeat-x;
+}
+
 QComboBox::separator {
 border: solid; border-width: 3px; border-color: #ffcc00;
 }
 
-QPushButton, QListWidget, QListView, QTableView, QLineEdit, QHeaderView,
+QPushButton, QListWidget, QListView, QTableView, QTableWidget, QLineEdit,
 QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
-.QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
-QTabWidget::pane, QTabBar::tab {
+.QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget,
+QTabWidget::pane, QTabBar::tab, #mapPreview, #labelLikeLineEdit {
 border: solid;
 border-width: 3px;
 border-color: #ffcc00;
@@ -56,28 +86,63 @@
 border-color: yellow;
 }
 
-QLineEdit, QListWidget, QListView,QTableView, QTextBrowser,
-QSpinBox, QToolBox, QPlainTextEdit {
+TeamShowWidget QPushButton {
+icon-size: 48px;
+text-align: left;
+background-color: #0d0544;
+color: orange;
+font: bold;
+border-width: 2px;
+margin: 6px 0px 6px 0px;
+}
+TeamShowWidget QPushButton:disabled {
+color: #a0a0a0;
+}
+
+QToolButton {
+background-color: #11084A;
+}
+
+QToolButton:hover {
+background-color: #150A61;
+}
+
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser,
+QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
 border-radius: 10px;
 }
 
+#mapPreview {
+background-color: #0d0544;
+}
+#mapPreview:disabled{
+border-color: #a0a0a0;
+background-color: #0d0544;
+color: #ffffff;
+}
+
 QLineEdit, QLabel, QHeaderView, QListWidget, QListView, QTableView,
-QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
-IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
+QTableWidget, QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
+IconedGroupBox, .QGroupBox, #gameStackContainer, TeamSelWidget,
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit,
+#mapName {
 font: bold 13px;
 }
+.QLabel{
+background-color: transparent;
+}
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
 background-position: bottom center;
 background-repeat: repeat-x;
 background-color: #000000;
 }
-.QGroupBox,GameCFGWidget,TeamSelWidget,SelWeaponWidget {
+.QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget {
 background-position: bottom center;
 background-repeat: repeat-x;
 border-radius: 16px;
 background-color: rgba(13, 5, 68, 70%);
 padding: 6px;
+margin-top: 4px;
 }
 /*  Experimenting with PaintOnScreen and border-radius on IconedGroupBox children didn't work out well
 IconedGroupBox QComboBox, IconedGroupBox QPushButton, IconedGroupBox QLineEdit,
@@ -89,6 +154,14 @@
 background-color: #130f2c;
 }
 
+QTabWidget::pane {
+border-radius: 8px;
+border-top-left-radius: 0px;
+}
+
+GameCFGWidget {
+border: none;
+}
 
 QPushButton {
 border-radius: 8px;
@@ -97,60 +170,102 @@
 background-color: rgba(18, 42, 5, 70%);
 }
 
-QPushButton:pressed{
+QPushButton:pressed, QToolButton:pressed {
 border-color: white;
+background-color: #344b1e;
+color: white;
+}
+TeamShowWidget QPushButton:pressed {
+background-color: #362c7a;
 }
 
 QPushButton:focus {
 outline: none;
 }
 
+QHeaderView {
+background-color: #00351d;
+border: solid;
+border-bottom-width: 3px;
+border-top-width: 0px;
+border-left-width: 0px;
+border-right-width: 0px;
+border-color: #ffcc00;
+}
+QHeaderView::section {
+border-left-width: 1px;
+border-right-width: 1px;
+border-top-width: 0;
+border-bottom-width: 0px;
+border-color: #001d10;
+border-style: solid;
+background-color: #005F35;
+padding: 4px;
+}
+QHeaderView::section:pressed {
+background-color: #005A33;
+border-color: #FFFFFF;
+}
+QHeaderView::up-arrow {
+image: url(":/res/sort_up.png");
+}
+QHeaderView::down-arrow{
+image: url(":/res/sort_down.png");
+}
 
-QHeaderView {
-border-radius: 0;
-border-width: 0;
-border-bottom-width: 3px;
-background-color: #00351d;
-}
-QTableView {
+QTableView, QTableWidget {
 alternate-background-color: #2f213a;
 gridline-color: transparent;
 }
-
+QTabWidget::pane { top: -2px; }
 QTabBar::tab {
-border-bottom-width: 0;
 border-radius: 0;
 border-top-left-radius: 6px;
 border-top-right-radius: 6px;
 padding: 3px;
+background-color: #00351d;
+color: #ffcc00;
 }
-QTabBar::tab:!selected {
-color: #0d0544;
-background-color: #ffcc00;
+QTabBar::tab:selected {
+border-bottom-color: #0d0544;
+border-bottom-width: 0;
 }
 QSpinBox::up-button{
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
 QSpinBox::up-arrow {
 image: url(":/res/spin_up.png");
 }
+QSpinBox::up-arrow:disabled {
+image: url(":/res/spin_up_disabled.png");
+}
 
 QSpinBox::down-arrow {
 image: url(":/res/spin_down.png");
 }
+QSpinBox::down-arrow:disabled {
+image: url(":/res/spin_down_disabled.png");
+}
 
 QSpinBox::down-button {
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
+QSpinBox, QLineEdit {
+padding: 3px;
+min-height: 18px;
+}
 QComboBox {
 border-radius: 10px;
 padding: 3px;
+height: 18px;
 }
 QComboBox:pressed{
 border-color: white;
@@ -162,6 +277,9 @@
 QComboBox::down-arrow {
 image: url(":/res/dropdown.png");
 }
+QComboBox::down-arrow:disabled {
+image: url(":/res/dropdown_disabled.png");
+}
 
 VertScrArea {
 background-position: bottom center;
@@ -178,15 +296,45 @@
 subcontrol-position: top left;
 text-align: left;
 left: 15px;
-top: -4px;
 }
 
 QCheckBox::indicator:checked{
 image: url(":/res/checked.png");
 }
+QCheckBox::indicator:checked:hover{
+image: url(":/res/checkedHover.png");
+}
+QCheckBox::indicator:checked:pressed{
+image: url(":/res/checkedPressed.png");
+}
 QCheckBox::indicator:unchecked{
 image: url(":/res/unchecked.png");
 }
+QCheckBox::indicator:unchecked:hover{
+image: url(":/res/uncheckedHover.png");
+}
+QCheckBox::indicator:unchecked:pressed{
+image: url(":/res/uncheckedPressed.png");
+}
+
+QRadioButton::indicator:checked{
+image: url(":/res/radioButtonChecked.png");
+}
+QRadioButton::indicator:checked:hover{
+image: url(":/res/radioButtonCheckedHover.png");
+}
+QRadioButton::indicator:checked:pressed{
+image: url(":/res/radioButtonCheckedPressed.png");
+}
+QRadioButton::indicator:unchecked{
+image: url(":/res/radioButtonUnchecked.png");
+}
+QRadioButton::indicator:unchecked:hover{
+image: url(":/res/radioButtonUncheckedHover.png");
+}
+QRadioButton::indicator:unchecked:pressed{
+image: url(":/res/radioButtonUncheckedPressed.png");
+}
 
 .QWidget{
 background: transparent;
@@ -227,6 +375,11 @@
 
 :disabled{
 color: #a0a0a0;
+border-color: #a0a0a0;
+}
+QListWidget:item:selected:disabled, QListView:item:selected:disabled{
+color: rgba(13, 5, 68, 70%);
+background-color: #a0a0a0;
 }
 SquareLabel, ItemNum {
 background-color: #000000;
@@ -237,12 +390,177 @@
 margin: 2px 0px;
 background-color: #ffcc00;
 }
+QSlider::groove::horizontal:disabled {
+background-color: #a0a0a0;
+}
 
 QSlider::handle::horizontal {
 border: 0px;
-margin: -2px 0px;
+margin: -8px 0px;
+background-color: #ffcc00;
+width: 12px;
+height: 6px;
 border-radius: 3px;
-background-color: #ffcc00;
-width: 8px;
+}
+QSlider::handle::horizontal:disabled {
+background-color: #a0a0a0;
+}
+
+HatButton, ThemeButton {
+text-align: left;
+}
+
+#hatList, #hatList:hover, #themeList, #themeList:hover {
+border-color: #F6CB1C;
+}
+
+QScrollBar:vertical {
+border: none;
+color: #FFD902;
+background: #00321c;
+width: 15px;
+margin: 17px 0 17px 0;
+}
+QScrollBar:horizontal {
+border: none;
+color: #FFD902;
+background: #00321c;
+height: 15px;
+margin: 0 17px 0 17px;
+}
+QScrollBar::handle:vertical {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-height: 20px;
+}
+QScrollBar::handle:horizontal {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-width: 20px;
+}
+QScrollBar::handle:pressed {
+background: #005a33;
+border-color: #FFFFFF;
+}
+QScrollBar::add-line, QScrollBar::sub-line {
+border: 1px solid #005F35;
+border-radius: 2px;
+background: #00321c;
+subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical {
+height: 15px;
+subcontrol-position: bottom;
+}
+QScrollBar::sub-line:vertical {
+height: 15px;
+subcontrol-position: top;
+}
+QScrollBar::add-line:horizontal {
+width: 15px;
+subcontrol-position: right;
+}
+QScrollBar::sub-line:horizontal {
+width: 15px;
+subcontrol-position: left;
+}
+QScrollBar::add-line:pressed, QScrollBar::sub-line:pressed {
+background: #005a33;
+border-color: #FFFFFF;
 }
 
+QScrollBar::up-arrow {
+background-image: url(":/res/scroll_up.png");
+}
+QScrollBar::down-arrow {
+background-image: url(":/res/scroll_down.png");
+}
+QScrollBar::left-arrow {
+background-image: url(":/res/scroll_left.png");
+}
+QScrollBar::right-arrow {
+background-image: url(":/res/scroll_right.png");
+}
+QScrollBar::add-page, QScrollBar::sub-page {
+background: #00190F;
+}
+QScrollBar::add-page:pressed, QScrollBar::sub-page:pressed {
+background: #008751;
+}
+#hatList, #themeList {
+border-color: #F6CB1C;
+border-width: 3px;
+border-style: solid;
+border-radius: 10px;
+border-top-left-radius: 0px;
+}
+
+#hatList::item, #themeList::item {
+background-color: #11084A;
+padding: 4px;
+border-radius: 10px;
+color: #ffcc00 !important;
+font: 8px;
+border-width: 2px;
+border-color: #11084A;
+}
+
+#hatList::item:hover, #themeList::item:hover {
+background-color: #150A61;
+}
+
+#hatList::item:selected, #themeList::item:selected {
+background-color: #150A61;
+}
+
+.QPushButton, .QPushButtonWithSound {
+padding: 3px 5px;
+}
+
+#gameCfgWidgetTabs {
+border-radius: 16px;
+border-top-left-radius: 0px;
+}
+
+TeamSelWidget, #gameStackContainer, #GBoxOptions {
+border-radius: 10px;
+}
+
+PageMultiplayer TeamSelWidget {
+min-height: 500px;
+}
+
+QProgressBar {
+border: 3px solid #FFCC00;
+background-color: #150A61;
+border-radius: 5px;
+text-align: center;
+color: #FFFFFF;
+}
+QProgressBar::chunk {
+background-color: #FF9B00;
+margin: 2px;
+}
+#keyBinderScrollArea {
+background: #130F2A;
+}
+#chatContainer {
+border-width: 0px;
+background-color: #ffcc00;
+border-radius: 10px;
+}
+#chatText {
+background-color: rgb(23, 11, 54);
+border-width: 0px;
+}
+#trainingList {
+border-style: none;
+padding-top: 6px;
+}
+#trainingList::item
+{
+padding-top: 2px;
+padding-bottom: 2px;
+}
--- a/QTfrontend/res/css/qt.css	Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/res/css/qt.css	Wed Jul 31 23:14:27 2019 +0200
@@ -1,10 +1,10 @@
 /******************************************************************************
  *
- * CSS-like definition of Qt frontend appearance
+ * CSS definition of Qt frontend appearance: Default style.
  *
  ******************************************************************************
  *
- * see http://doc.qt.nokia.com/4.5/stylesheet.html
+ * see https://doc.qt.io/qt-5/style-reference.html
  *
  ******************************************************************************
  *
@@ -93,6 +93,9 @@
 TeamShowWidget QPushButton:disabled {
 color: #a0a0a0;
 }
+TeamShowWidget QPushButton:pressed {
+background-color: #362c7a;
+}
 
 QToolButton {
 background-color: #11084A;
@@ -102,10 +105,6 @@
 background-color: #150A61;
 }
 
-QToolButton:pressed {
-background-color: #100744;
-}
-
 QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser,
 QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
 border-radius: 10px;
@@ -171,6 +170,8 @@
 
 QPushButton:pressed, QToolButton:pressed {
 border-color: white;
+background-color: #344b1e;
+color: white;
 }
 
 QPushButton:focus {
@@ -191,13 +192,14 @@
 border-right-width: 1px;
 border-top-width: 0;
 border-bottom-width: 0px;
-border-color: #001d10;
+border-color: #005F35;
 border-style: solid;
 background-color: #00351d;
 padding: 4px;
 }
 QHeaderView::section:pressed {
-background-color: #00250d;
+background-color: #005A33;
+border-color: #FFFFFF;
 }
 QHeaderView::up-arrow {
 image: url(":/res/sort_up.png");
@@ -227,6 +229,7 @@
 background: transparent;
 width: 16px;
 height: 10px;
+padding-top: 1px;
 }
 
 QSpinBox::up-arrow {
@@ -247,8 +250,13 @@
 background: transparent;
 width: 16px;
 height: 10px;
+padding-bottom: 1px;
 }
 
+QSpinBox, QLineEdit {
+padding: 3px;
+min-height: 18px;
+}
 QComboBox {
 border-radius: 10px;
 padding: 3px;
@@ -401,10 +409,80 @@
 border-color: #F6CB1C;
 }
 
-#hatList QScrollBar, #themeList QScrollBar {
-background-color: #130F2A;
-border-top-right-radius: 10px;
-border-bottom-right-radius: 10px;
+QScrollBar:vertical {
+border: none;
+color: #FFD902;
+background: #00321c;
+width: 15px;
+margin: 17px 0 17px 0;
+}
+QScrollBar:horizontal {
+border: none;
+color: #FFD902;
+background: #00321c;
+height: 15px;
+margin: 0 17px 0 17px;
+}
+QScrollBar::handle:vertical {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-height: 20px;
+}
+QScrollBar::handle:horizontal {
+background: #00321c;
+border: 1px solid #005F35;
+border-radius: 2px;
+min-width: 20px;
+}
+QScrollBar::handle:pressed {
+background: #005a33;
+border-color: #FFFFFF;
+}
+QScrollBar::add-line, QScrollBar::sub-line {
+border: 1px solid #005F35;
+border-radius: 2px;
+background: #00321c;
+subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical {
+height: 15px;
+subcontrol-position: bottom;
+}
+QScrollBar::sub-line:vertical {
+height: 15px;
+subcontrol-position: top;
+}
+QScrollBar::add-line:horizontal {
+width: 15px;
+subcontrol-position: right;
+}
+QScrollBar::sub-line:horizontal {
+width: 15px;
+subcontrol-position: left;
+}
+QScrollBar::add-line:pressed, QScrollBar::sub-line:pressed {
+background: #005a33;
+border-color: #FFFFFF;
+}
+
+QScrollBar::up-arrow {
+background-image: url(":/res/scroll_up.png");
+}
+QScrollBar::down-arrow {
+background-image: url(":/res/scroll_down.png");
+}
+QScrollBar::left-arrow {
+background-image: url(":/res/scroll_left.png");
+}
+QScrollBar::right-arrow {
+background-image: url(":/res/scroll_right.png");
+}
+QScrollBar::add-page, QScrollBar::sub-page {
+background: #00190F;
+}
+QScrollBar::add-page:pressed, QScrollBar::sub-page:pressed {
+background: #008751;
 }
 
 #hatList, #themeList {
@@ -433,7 +511,7 @@
 background-color: #150A61;
 }
 
-QDialogButtonBox QPushButton {
+.QPushButton, .QPushButtonWithSound {
 padding: 3px 5px;
 }
 
@@ -449,3 +527,36 @@
 PageMultiplayer TeamSelWidget {
 min-height: 500px;
 }
+
+QProgressBar {
+border: 3px solid #FFCC00;
+background-color: #150A61;
+border-radius: 5px;
+text-align: center;
+color: #FFFFFF;
+}
+QProgressBar::chunk {
+background-color: #FF9B00;
+margin: 2px;
+}
+#keyBinderScrollArea {
+background: #130F2A;
+}
+#chatContainer {
+border-width: 0px;
+background-color: #ffcc00;
+border-radius: 10px;
+}
+#chatText {
+background-color: rgb(23, 11, 54);
+border-width: 0px;
+}
+#trainingList {
+border-style: none;
+padding-top: 6px;
+}
+#trainingList::item
+{
+padding-top: 2px;
+padding-bottom: 2px;
+}
--- a/QTfrontend/res/html/about.html	Wed May 16 18:22:28 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-<!DOCTYPE HTML>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head>
-<title>Hedgewars - Authors</title>
-<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-<style type="text/css">
-     body { color: orange; }
-     a { color: #ffe270; }
-     a:hover { color: yellow; }
-</style>
-</head>
-<body>
-    <h2>Developers:</h2>
-    <p>
-        Engine, frontend, net server: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;<br />
-        Many frontend improvements: Igor Ulyanov &lt;<a href="mailto:disinbox@gmail.com">disinbox@gmail.com</a>&gt;<br />
-        Many engine and frontend improvements: Derek Pomery &lt;<a href="mailto:nemo@m8y.org">nemo@m8y.org</a>&gt;<br />
-        Drill rocket, Ballgun, RC Plane weapons: Martin Boze &lt;<a href="mailto:afffect@gmail.com">afffect@gmail.com</a>&gt;<br />
-        Mine number and time game settings: David A. Cuadrado &lt;<a href="mailto:krawek@gmail.com">krawek@gmail.com</a>&gt;<br />
-        Frontend improvements: Martin Minarik &lt;<a href="mailto:ttsmj@pokec.sk">ttsmj@pokec.sk</a>&gt;<br />
-        Frontend improvements: Kristian Lehmann &lt;<a href="mailto:email@thexception.net">email@thexception.net</a>&gt;<br />
-        Mac OS X/iPhone port, OpenGL-ES conversion: Vittorio Giovara &lt;<a href="mailto:vittorio.giovara@gmail.com">vittorio.giovara@gmail.com</a>&gt;<br />
-        Many engine and frontend improvements (and bugs): Richard Karolyi &lt;<a href="mailto:sheepluva@ercatec.net">sheepluva@ercatec.net</a>&gt;<br />
-        Gamepad and Lua integration: Mario Liebisch &lt;<a href="mailto:mario.liebisch@gmail.com">mario.liebisch@gmail.com</a>&gt;<br />
-        Many engine improvements and graphics: Carlos Vives &lt;<a href="mailto:mail@carlosvives.es">mail@carlosvives.es</a>&gt;<br />
-        Maze maps: Henning K&uuml;hn &lt;<a href="mailto:prg@cooco.de">prg@cooco.de</a>&gt;<br />
-        Engine and frontend improvements: Henrik Rostedt &lt;<a href="mailto:henrik.rostedt@gmail.com">henrik.rostedt@gmail.com</a>&gt;<br />
-        Lua game modes and missions: John Lambert &lt;<a href="mailto:redgrinner@gmail.com">redgrinner@gmail.com</a>&gt;<br />
-        Frontend improvements: Mayur Pawashe &lt;<a href="mailto:zorgiepoo@gmail.com">zorgiepoo@gmail.com</a>&gt;<br />
-        Android port: Richard Deurwaarder &lt;<a href="mailto:xeli@xelification.com">xeli@xelification.com</a>&gt;<br />
-        Android netplay, portability abstraction: Simeon Maxein &lt;<a href="mailto:smaxein@googlemail.com">smaxein@googlemail.com</a>&gt;<br />
-        WebGL port, some pas2c and GLES2 work: Meng Xiangyun &lt;<a href="mailto:xymengxy@gmail.com">xymengxy@gmail.com</a>&gt;<br />
-        Video recording: Stepan Podoskin &lt;<a href="mailto:stepik-777@mail.ru">stepik-777@mail.ru</a>&gt;<br />
-        Campaign support, first campaign: Szabolcs Orb&agrave;n &lt;<a href="mailto:szabibibi@gmail.com">szabibibi@gmail.com</a>&gt;<br />
-        Keybinds, feedback, maps and hats interfaces: Drew Gottlieb &lt;<a href="mailto:gottlieb.drew@gmail.com">gottlieb.drew@gmail.com</a>&gt;<br />
-        Login dialogs, frontend improvements: Ondrej Skopek &lt;<a href="mailto:skopekondrej@gmail.com">skopekondrej@gmail.com</a>&gt;<br />
-        Icegun weapon: Julia Struchenko &lt;<a href="mailto:urbertar@gmail.com">urbertar@gmail.com</a>&gt;<br />
-        iPhone/iPad ports: Anton Malmygin &lt;<a href="mailto:antonc27@mail.ru">antonc27@mail.ru</a>&gt;<br />
-        Battalion style: Anachron &lt;<a href="mailto:Anachron14@gmx.de">Anachron14@gmx.de</a>&gt;<br />
-        Scripting, engine, frontend improvements, some missions: Wuzzy &lt;<a href="mailto:Wuzzy2@mail.ru">Wuzzy2@mail.ru</a>&gt;<br />
-        Theme customization improvements: KoBeWi<br />
-        Theme music, engine and frontend improvements, graphics: Valentin Kraevskiy <!--&lt;<a href="mailto:foo@bar.com">foo@bar.com</a>&gt;--><br />
-        </p>
-
-        <h2>Art:</h2>
-            <p>John Dum &lt;<a href="mailto:fizzy@gmail.com">fizzy@gmail.com</a>&gt;
-            <br />
-            Joshua Frese &lt;<a href="mailto:joshfrese@gmail.com&