diff --git a/CI/conan/base/android b/CI/conan/base/android index 931061b30..507c18bbc 100644 --- a/CI/conan/base/android +++ b/CI/conan/base/android @@ -6,4 +6,5 @@ compiler.version=14 os=Android [buildenv] -LD=ld # fixes shared libiconv build +# fixes shared libiconv build +LD=ld diff --git a/client/eventsSDL/InputHandler.cpp b/client/eventsSDL/InputHandler.cpp index 7d2bf4eb7..9f02fd966 100644 --- a/client/eventsSDL/InputHandler.cpp +++ b/client/eventsSDL/InputHandler.cpp @@ -237,7 +237,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev) #endif break; case SDL_WINDOWEVENT_SIZE_CHANGED: -#ifdef VCMI_ANDROID +#ifdef VCMI_MOBILE { std::scoped_lock interfaceLock(ENGINE->interfaceMutex); ENGINE->onScreenResize(true); diff --git a/client/lobby/OptionsTab.cpp b/client/lobby/OptionsTab.cpp index e39a1bfc8..6934785cb 100644 --- a/client/lobby/OptionsTab.cpp +++ b/client/lobby/OptionsTab.cpp @@ -69,13 +69,14 @@ void OptionsTab::recreate() entries.clear(); humanPlayers = 0; + for (auto tooltipWindow : ENGINE->windows().findWindows()) + tooltipWindow->close(); + for (auto heroOverview : ENGINE->windows().findWindows()) heroOverview->close(); for (auto selectionWindow : ENGINE->windows().findWindows()) - { selectionWindow->reopen(); - } OBJECT_CONSTRUCTION; for(auto & pInfo : SEL->getStartInfo()->playerInfos) diff --git a/client/renderSDL/CTrueTypeFont.cpp b/client/renderSDL/CTrueTypeFont.cpp index 9d5e8fea8..782ae1e85 100644 --- a/client/renderSDL/CTrueTypeFont.cpp +++ b/client/renderSDL/CTrueTypeFont.cpp @@ -147,7 +147,8 @@ void CTrueTypeFont::renderTextImpl(SDL_Surface * surface, const std::string & te else rendered = TTF_RenderUTF8_Solid(font.get(), text.c_str(), CSDL_Ext::toSDL(color)); - assert(rendered); + if (!rendered) + throw std::runtime_error("Failed to render text '" + text + "'. Reason: '" + TTF_GetError() + "'"); CSDL_Ext::blitSurface(rendered, surface, pos); SDL_FreeSurface(rendered); diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 26f301676..fa49ee327 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -58,13 +58,14 @@ std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & auto it = animationFiles.find(actualPath); if (it != animationFiles.end()) - return it->second; + { + auto locked = it->second.lock(); + if (locked) + return locked; + } if (!CResourceHandler::get()->existsResource(actualPath)) - { - animationFiles[actualPath] = nullptr; return nullptr; - } auto result = std::make_shared(actualPath); @@ -193,7 +194,11 @@ std::shared_ptr RenderHandler::loadImageImpl(const ImageLoc { auto it = imageFiles.find(locator); if (it != imageFiles.end()) - return it->second; + { + auto locked = it->second.lock(); + if (locked) + return locked; + } auto sdlImage = loadImageFromFileUncached(locator); auto scaledImage = std::make_shared(locator, sdlImage); diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 72dcf2384..e2f24b901 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -24,9 +24,9 @@ class RenderHandler final : public IRenderHandler { using AnimationLayoutMap = std::map>; - std::map> animationFiles; + std::map> animationFiles; std::map animationLayouts; - std::map> imageFiles; + std::map> imageFiles; std::map> fonts; std::unique_ptr assetGenerator; diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index e912275a0..bc1554e48 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -246,7 +246,7 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL else algorithm = EScalingAlgorithm::XBRZ_ALPHA; - auto result = std::make_shared(this, factor, algorithm); + auto result = SDLImageShared::createScaled(this, factor, algorithm); if (surf->format->palette) SDL_SetSurfacePalette(surf, originalPalette); @@ -254,26 +254,28 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL return result; } -SDLImageShared::SDLImageShared(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm) +std::shared_ptr SDLImageShared::createScaled(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm) { - upscalingInProgress = true; + auto self = std::make_shared(nullptr); + self->upscalingInProgress = true; auto scaler = std::make_shared(from->surf, Rect(from->margins, from->fullSize), true); - const auto & scalingTask = [this, algorithm, scaler]() + const auto & scalingTask = [self, algorithm, scaler]() { scaler->scaleSurfaceIntegerFactor(ENGINE->screenHandler().getScalingFactor(), algorithm); - surf = scaler->acquireResultSurface(); - fullSize = scaler->getResultDimensions().dimensions(); - margins = scaler->getResultDimensions().topLeft(); - - upscalingInProgress = false; + self->surf = scaler->acquireResultSurface(); + self->fullSize = scaler->getResultDimensions().dimensions(); + self->margins = scaler->getResultDimensions().topLeft(); + self->upscalingInProgress = false; }; if(settings["video"]["asyncUpscaling"].Bool()) ENGINE->async().run(scalingTask); else scalingTask(); + + return self; } bool SDLImageShared::isLoading() const @@ -361,9 +363,10 @@ Rect SDLImageShared::contentRect() const if(upscalingInProgress) throw std::runtime_error("Attempt to access images that is still being loaded!"); - auto tmpMargins = margins; - auto tmpSize = Point(surf->w, surf->h); - return Rect(tmpMargins, tmpSize); + if (!surf) + return Rect(); + + return Rect(margins, Point(surf->w, surf->h)); } const SDL_Palette * SDLImageShared::getPalette() const diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 2ad465c67..bd81d01cf 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -49,10 +49,11 @@ public: SDLImageShared(const ImagePath & filename); //Create using existing surface, extraRef will increase refcount on SDL_Surface SDLImageShared(SDL_Surface * from); - /// Creates image at specified scaling factor from source image - SDLImageShared(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm); ~SDLImageShared(); + /// Creates image at specified scaling factor from source image + static std::shared_ptr createScaled(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm); + void scaledDraw(SDL_Surface * where, SDL_Palette * palette, const Point & scaling, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override; void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override; diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 843c4dad8..1a1d966aa 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -80,7 +80,7 @@ SDL_Surface * CSDL_Ext::newSurface(const Point & dimensions, SDL_Surface * mod) std::string messagePattern = "Failed to create SDL Surface of size %d x %d. Reason: %s"; std::string message = boost::str(boost::format(messagePattern) % dimensions.x % dimensions.y % error); - handleFatalError(message, true); + throw std::runtime_error(message); } if (mod && mod->format->palette) diff --git a/client/renderSDL/ScreenHandler.cpp b/client/renderSDL/ScreenHandler.cpp index eca366f9c..3a1bc074b 100644 --- a/client/renderSDL/ScreenHandler.cpp +++ b/client/renderSDL/ScreenHandler.cpp @@ -373,7 +373,7 @@ EUpscalingFilter ScreenHandler::loadUpscalingFilter() const if (scaling <= 1.001f) return EUpscalingFilter::NONE; // running at original resolution or even lower than that - no need for xbrz - if (systemMemoryMb < 2048) + if (systemMemoryMb <= 4096) return EUpscalingFilter::NONE; // xbrz2 may use ~1.0 - 1.5 Gb of RAM and has notable CPU cost - avoid on low-spec hardware // Only using xbrz2 for autoselection. diff --git a/clientapp/EntryPoint.cpp b/clientapp/EntryPoint.cpp index 6ab52e40f..1485ed4dc 100644 --- a/clientapp/EntryPoint.cpp +++ b/clientapp/EntryPoint.cpp @@ -288,9 +288,10 @@ int main(int argc, char * argv[]) testFile("DATA/HELP.TXT", "VCMI requires Heroes III: Shadow of Death or Heroes III: Complete data files to run!"); testFile("DATA/TENTCOLR.TXT", "Heroes III: Restoration of Erathia (including HD Edition) data files are not supported!"); - testFile("MODS/VCMI/MOD.JSON", "VCMI installation is corrupted! Built-in mod was not found!"); - testFile("DATA/PLAYERS.PAL", "Heroes III data files (Data/H3Bitmap.lod) are incomplete or corruped! Please reinstall them."); - testFile("SPRITES/DEFAULT.DEF", "Heroes III data files (Data/H3Sprite.lod) are incomplete or corruped! Please reinstall them."); + testFile("MODS/VCMI/MOD.JSON", "VCMI installation is corrupted!\nBuilt-in mod was not found!"); + testFile("DATA/NOTOSERIF-MEDIUM.TTF", "VCMI installation is corrupted!\nBuilt-in font was not found!\nManually deleting '" + VCMIDirs::get().userDataPath().string() + "/Mods/VCMI' directory (if it exists)\nor clearing app data and reimporting Heroes III files may fix this problem."); + testFile("DATA/PLAYERS.PAL", "Heroes III data files (Data/H3Bitmap.lod) are incomplete or corruped!\n Please reinstall them."); + testFile("SPRITES/DEFAULT.DEF", "Heroes III data files (Data/H3Sprite.lod) are incomplete or corruped!\n Please reinstall them."); srand ( (unsigned int)time(nullptr) ); diff --git a/clientapp/ios/Info.plist b/clientapp/ios/Info.plist index 0fba21025..32d1f671d 100644 --- a/clientapp/ios/Info.plist +++ b/clientapp/ios/Info.plist @@ -53,10 +53,5 @@ UIStatusBarHidden - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 277b1267a..76fde0eb1 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -659,14 +659,15 @@ "additionalProperties" : false, "required" : [ "setupCompleted", - "defaultRepositoryEnabled", - "defaultRepositoryURL", - "extraRepositoryURL", - "extraRepositoryEnabled", - "autoCheckRepositories", + "defaultRepositoryEnabled", + "defaultRepositoryURL", + "extraRepositoryURL", + "extraRepositoryEnabled", + "autoCheckRepositories", "ignoreSslErrors", - "updateOnStartup", - "updateConfigUrl" + "updateOnStartup", + "updateConfigUrl", + "trackClipboardState" ], "properties" : { "defaultRepositoryEnabled" : { @@ -704,7 +705,15 @@ "updateConfigUrl" : { "type" : "string", "default" : "https://raw.githubusercontent.com/vcmi/vcmi-updates/master/vcmi-updates.json" - } + }, + "trackClipboardState" : { + "type" : "boolean", + "default" : true, + "defaultIOS": false, + "defaultAndroid": false, + "defaultDesktop" : true + + }, } }, "lobby" : { diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 9b173d410..065596946 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -32,6 +32,11 @@ if(APPLE_IOS) ios/revealdirectoryinfiles.mm ios/selectdirectory.h ios/selectdirectory.mm + prepare_ios.mm + ) +elseif(ANDROID) + list(APPEND launcher_SRCS + prepare_android.cpp ) endif() @@ -57,6 +62,7 @@ set(launcher_HEADERS helper.h innoextract.h prepare.h + prepare_p.h ) set(launcher_FORMS diff --git a/launcher/main.cpp b/launcher/main.cpp index 019ee87e6..dfef9f96b 100644 --- a/launcher/main.cpp +++ b/launcher/main.cpp @@ -108,21 +108,36 @@ void startEditor(const QStringList & args) #ifndef VCMI_MOBILE void startExecutable(QString name, const QStringList & args) { + QProcess process; + auto showError = [&] { + QMessageBox::critical(qApp->activeWindow(), + QObject::tr("Error starting executable"), + QObject::tr("Failed to start %1\nReason: %2").arg(name, process.errorString())); + }; + +#if defined(VCMI_MAC) || defined(VCMI_WINDOWS) + if(process.startDetached(name, args)) + { + qApp->quit(); + } + else + { + showError(); + } +#else // Linux // Start vcmiclient and vcmieditor with QProcess::start() instead of QProcess::startDetached() // since startDetached() results in a missing terminal prompt after quitting vcmiclient. // QProcess::start() causes the launcher window to freeze while the child process is running, so we hide it in // MainWindow::on_startGameButton_clicked() and MainWindow::on_startEditorButton_clicked() - QProcess process; process.setProcessChannelMode(QProcess::ForwardedChannels); process.start(name, args); process.waitForFinished(-1); if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) { - QMessageBox::critical(qApp->activeWindow(), - QObject::tr("Error starting executable"), - QObject::tr("Failed to start %1\nReason: %2").arg(name, process.errorString())); + showError(); } qApp->quit(); +#endif } #endif diff --git a/launcher/prepare.cpp b/launcher/prepare.cpp index 7e158d4ac..f971bf644 100644 --- a/launcher/prepare.cpp +++ b/launcher/prepare.cpp @@ -9,75 +9,17 @@ */ #include "StdInc.h" #include "prepare.h" +#include "prepare_p.h" #include "../vcmiqt/launcherdirs.h" -#include -#include -#include - -#ifdef VCMI_ANDROID -#include "../lib/CAndroidVMHelper.h" - -#include -#include -#include - -namespace -{ -// https://gist.github.com/ssendeavour/7324701 -bool copyRecursively(const QString &srcFilePath, const QString &tgtFilePath) -{ - QFileInfo srcFileInfo{srcFilePath}; - if(srcFileInfo.isDir()) { - QDir targetDir{tgtFilePath}; - targetDir.cdUp(); - if(!targetDir.mkpath(QFileInfo{tgtFilePath}.fileName())) - return false; - targetDir.setPath(tgtFilePath); - - QDir sourceDir{srcFilePath}; - const auto fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); - for(const auto & fileName : fileNames) { - const auto newSrcFilePath = sourceDir.filePath(fileName); - const auto newTgtFilePath = targetDir.filePath(fileName); - if(!copyRecursively(newSrcFilePath, newTgtFilePath)) - return false; - } - } else { - if(!QFile::copy(srcFilePath, tgtFilePath)) - return false; - } - return true; -} - -void prepareAndroid() -{ - QAndroidJniEnvironment jniEnv; - CAndroidVMHelper::initClassloader(static_cast(jniEnv)); - - const bool justLaunched = QtAndroid::androidActivity().getField("justLaunched") == JNI_TRUE; - if(!justLaunched) - return; - - // copy core data to internal directory - const auto vcmiDir = QAndroidJniObject::callStaticObjectMethod("eu/vcmi/vcmi/NativeMethods", "internalDataRoot").toString(); - for(auto vcmiFilesResource : {QLatin1String{"config"}, QLatin1String{"Mods"}}) - { - QDir destDir = QString{"%1/%2"}.arg(vcmiDir, vcmiFilesResource); - destDir.removeRecursively(); - copyRecursively(QString{":/%1"}.arg(vcmiFilesResource), destDir.absolutePath()); - } -} -} -#endif - - namespace launcher { void prepare() { #ifdef VCMI_ANDROID prepareAndroid(); +#elif defined(VCMI_IOS) + prepareIos(); #endif CLauncherDirs::prepare(); diff --git a/launcher/prepare_android.cpp b/launcher/prepare_android.cpp new file mode 100644 index 000000000..0a25b3972 --- /dev/null +++ b/launcher/prepare_android.cpp @@ -0,0 +1,71 @@ +/* + * prepare_android.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "prepare_p.h" +#include "../lib/CAndroidVMHelper.h" + +#include +#include +#include + +#include +#include +#include + +namespace +{ +// https://gist.github.com/ssendeavour/7324701 +bool copyRecursively(const QString & srcFilePath, const QString & tgtFilePath) +{ + QFileInfo srcFileInfo{srcFilePath}; + if(srcFileInfo.isDir()) { + QDir targetDir{tgtFilePath}; + targetDir.cdUp(); + if(!targetDir.mkpath(QFileInfo{tgtFilePath}.fileName())) + return false; + targetDir.setPath(tgtFilePath); + + QDir sourceDir{srcFilePath}; + const auto fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + for(const auto & fileName : fileNames) { + const auto newSrcFilePath = sourceDir.filePath(fileName); + const auto newTgtFilePath = targetDir.filePath(fileName); + if(!copyRecursively(newSrcFilePath, newTgtFilePath)) + return false; + } + } else { + if(!QFile::copy(srcFilePath, tgtFilePath)) + return false; + } + return true; +} +} + +namespace launcher +{ +void prepareAndroid() +{ + QAndroidJniEnvironment jniEnv; + CAndroidVMHelper::initClassloader(static_cast(jniEnv)); + + const bool justLaunched = QtAndroid::androidActivity().getField("justLaunched") == JNI_TRUE; + if(!justLaunched) + return; + + // copy core data to internal directory + const auto vcmiDir = QAndroidJniObject::callStaticObjectMethod("eu/vcmi/vcmi/NativeMethods", "internalDataRoot").toString(); + for(auto vcmiFilesResource : {QLatin1String{"config"}, QLatin1String{"Mods"}}) + { + QDir destDir = QString{"%1/%2"}.arg(vcmiDir, vcmiFilesResource); + destDir.removeRecursively(); + copyRecursively(QString{":/%1"}.arg(vcmiFilesResource), destDir.absolutePath()); + } +} +} diff --git a/launcher/prepare_ios.mm b/launcher/prepare_ios.mm new file mode 100644 index 000000000..25f4d2c79 --- /dev/null +++ b/launcher/prepare_ios.mm @@ -0,0 +1,40 @@ +/* + * prepare_ios.mm, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "prepare_p.h" + +#import + +#include + +namespace +{ +UIInterfaceOrientationMask swizzled_supportedInterfaceOrientationsForWindow + (id __unused self, SEL __unused _cmd, UIApplication * __unused application, UIWindow * __unused _Nullable window) +{ + if(UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) + return UIInterfaceOrientationMaskAll; + return UIInterfaceOrientationMaskLandscape; +} +} + +namespace launcher +{ +void prepareIos() +{ + auto sel = @selector(application:supportedInterfaceOrientationsForWindow:); + auto methodDesc = protocol_getMethodDescription(@protocol(UIApplicationDelegate), sel, NO, YES); + auto appDelegateClass = object_getClass(UIApplication.sharedApplication.delegate); + [[maybe_unused]] auto existingImp = class_replaceMethod( + appDelegateClass, sel, (IMP)swizzled_supportedInterfaceOrientationsForWindow, methodDesc.types); + // also check implementation in qtbase - src/plugins/platforms/ios/qiosapplicationdelegate.mm + NSCAssert(existingImp == nullptr, @"original app delegate has this method, don't ignore it"); +} +} diff --git a/launcher/prepare_p.h b/launcher/prepare_p.h new file mode 100644 index 000000000..904353cea --- /dev/null +++ b/launcher/prepare_p.h @@ -0,0 +1,19 @@ +/* + * prepare_p.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +namespace launcher +{ +#ifdef VCMI_ANDROID +void prepareAndroid(); +#elif defined(VCMI_IOS) +void prepareIos(); +#endif +} diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index 6a1852ce7..f8fc3a30e 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/launcher/settingsView/csettingsview_moc.cpp @@ -113,6 +113,8 @@ void CSettingsView::loadSettings() ui->labelHapticFeedback->hide(); ui->labelResetTutorialTouchscreen->hide(); ui->pushButtonResetTutorialTouchscreen->hide(); + ui->labelAllowPortrait->hide(); + ui->buttonAllowPortrait->hide(); if (settings["video"]["realFullscreen"].Bool()) ui->comboBoxFullScreen->setCurrentIndex(2); else @@ -121,8 +123,6 @@ void CSettingsView::loadSettings() #ifndef VCMI_ANDROID ui->buttonHandleBackRightMouseButton->hide(); ui->labelHandleBackRightMouseButton->hide(); - ui->buttonAllowPortrait->hide(); - ui->labelAllowPortrait->hide(); #endif #ifndef VCMI_IOS ui->labelIgnoreMuteSwitch->hide(); diff --git a/launcher/startGame/StartGameTab.cpp b/launcher/startGame/StartGameTab.cpp index 5242d362d..3ebd1c75a 100644 --- a/launcher/startGame/StartGameTab.cpp +++ b/launcher/startGame/StartGameTab.cpp @@ -51,9 +51,11 @@ StartGameTab::StartGameTab(QWidget * parent) ui->buttonGameEditor->hide(); #endif - auto clipboard = QGuiApplication::clipboard(); - - connect(clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged())); + if (settings["launcher"]["trackClipboardState"].Bool()) + { + auto clipboard = QGuiApplication::clipboard(); + connect(clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged())); + } } void StartGameTab::clipboardDataChanged() @@ -96,7 +98,8 @@ void StartGameTab::refreshState() refreshPresets(); refreshMods(); - clipboardDataChanged(); + if (settings["launcher"]["trackClipboardState"].Bool()) + clipboardDataChanged(); } void StartGameTab::refreshPresets() @@ -398,9 +401,22 @@ void StartGameTab::on_buttonPresetExport_clicked() void StartGameTab::on_buttonPresetImport_clicked() { QString presetString = QGuiApplication::clipboard()->text(); + + if (!presetString.startsWith("{")) + { + MessageBoxCustom::information(this, tr("Preset import failed"), tr("Failed to import preset - data in clipboard does not looks like mod preset!")); + return; + } + QByteArray presetBytes(presetString.toUtf8()); JsonNode presetJson(reinterpret_cast(presetBytes.data()), presetBytes.size(), "imported preset"); + if (presetJson["name"].String().empty() || presetJson["mods"].Vector().empty()) + { + MessageBoxCustom::information(this, tr("Preset import failed"), tr("Failed to import preset - data in clipboard does not looks like mod preset!")); + return; + } + Helper::getMainWindow()->getModView()->importPreset(presetJson); Helper::getMainWindow()->switchToModsTab(); refreshPresets(); diff --git a/launcher/translation/chinese.ts b/launcher/translation/chinese.ts index 92144f170..099d9f1aa 100644 --- a/launcher/translation/chinese.ts +++ b/launcher/translation/chinese.ts @@ -1268,7 +1268,8 @@ Bin (%n字节): Czech - + AI-generated, needs review by native speaker; delete this comment afterwards + 捷克语 @@ -1278,77 +1279,92 @@ Bin (%n字节): English - + AI-generated, needs review by native speaker; delete this comment afterwards + 英语 Finnish - + AI-generated, needs review by native speaker; delete this comment afterwards + 芬兰语 French - + AI-generated, needs review by native speaker; delete this comment afterwards + 法语 German - + AI-generated, needs review by native speaker; delete this comment afterwards + 德语 Hungarian - + AI-generated, needs review by native speaker; delete this comment afterwards + 匈牙利语 Italian - + AI-generated, needs review by native speaker; delete this comment afterwards + 意大利语 Korean - + AI-generated, needs review by native speaker; delete this comment afterwards + 韩语 Polish - + AI-generated, needs review by native speaker; delete this comment afterwards + 波兰语 Portuguese - + AI-generated, needs review by native speaker; delete this comment afterwards + 葡萄牙语 Russian - + AI-generated, needs review by native speaker; delete this comment afterwards + 俄语 Spanish - + AI-generated, needs review by native speaker; delete this comment afterwards + 西班牙语 Swedish - + AI-generated, needs review by native speaker; delete this comment afterwards + 瑞典语 Turkish - + AI-generated, needs review by native speaker; delete this comment afterwards + 土耳其语 Ukrainian - + AI-generated, needs review by native speaker; delete this comment afterwards + 乌克兰语 Vietnamese - + AI-generated, needs review by native speaker; delete this comment afterwards + 越南语 diff --git a/launcher/translation/french.ts b/launcher/translation/french.ts index 8f62ca06d..a6b644ca1 100644 --- a/launcher/translation/french.ts +++ b/launcher/translation/french.ts @@ -282,17 +282,17 @@ This mod cannot be enabled because it translates into a different language. - + Ce mod ne peut pas être activé car il traduit dans une langue différente. This mod can not be enabled because the following dependencies are not present - + Ce mod ne peut pas être activé car les dépendances suivantes sont manquantes This mod can not be installed because the following dependencies are not present - + Ce mod ne peut pas être installé car les dépendances suivantes sont manquantes @@ -308,17 +308,17 @@ Context menu - + Menu contextuel Open directory - + Ouvrir le répertoire Open repository - + Ouvrir le dépôt @@ -355,7 +355,7 @@ Installer les téchargements réussis? Installing Heroes Chronicles - + Installation de Heroes Chronicles @@ -463,7 +463,7 @@ Installer les téchargements réussis? Mods Validation - + Validation des mods @@ -488,17 +488,17 @@ Installer les téchargements réussis? Full - + Complet Allow portrait mode - + Autoriser le mode portrait Use scalable fonts - + Utiliser des polices redimensionnables @@ -508,22 +508,22 @@ Installer les téchargements réussis? Handle back as right mouse button - + Gérer le retour en atnt que le bouton droit de la souris Cursor Scaling - + Mise à l'échelle du curseur Scalable - + Redimensionnable Miscellaneous - + Divers @@ -534,17 +534,23 @@ Windowed - the game will run inside a window that covers part of your screen. Borderless Windowed Mode - the game will run in a full-screen window, matching your screen's resolution. Fullscreen Exclusive Mode - the game will cover the entirety of your screen and will use selected resolution. - + Sélectionnez un mode d'affichage pour le jeu + +Fenêtré - le jeu s'exécutera dans une fenêtre couvrant une partie de votre écran. + +Mode Fenêtré Sans Bordures - le jeu s'exécutera en plein écran dans une fenêtre correspondant à la résolution de votre écran. + +Mode Plein Écran Exclusif - le jeu couvrira entièrement votre écran et utilisera la résolution sélectionnée. Font Scaling (experimental) - + Mise à l'échelle des polices (expérimental) Original - + Original @@ -554,7 +560,7 @@ Fullscreen Exclusive Mode - the game will cover the entirety of your screen and Basic - + Basique @@ -817,33 +823,33 @@ Fullscreen Exclusive Mode - the game will cover the entirety of your screen and Invalid file selected - Fichier sélectionné non valide + Fichier sélectionné invalide You have to select a Heroes Chronicles installer file! - + Vous devez sélectionner un fichier d'installation de Heroes Chronicles ! Extracting error! - Erreur d'extraction ! + Erreur d'extraction ! Hash error! - + Erreur de hachage ! Heroes Chronicles - + Heroes Chronicles Heroes Chronicles %1 - %2 - + Heroes Chronicles %1 - %2 @@ -891,7 +897,13 @@ Before you can start playing, there are a few more steps to complete. Please remember that to use VCMI, you must own the original data files for Heroes® of Might and Magic® III: Complete or The Shadow of Death. Heroes® of Might and Magic® III HD is currently not supported! - + Merci d'avoir installé VCMI ! + +Avant de pouvoir commencer à jouer, il y a encore quelques étapes à compléter. + +Veuillez noter que pour utiliser VCMI, vous devez posséder les fichiers de données originaux de Heroes® of Might and Magic® III: Complete ou The Shadow of Death. + +Heroes® of Might and Magic® III HD n'est pas actuellement pris en charge ! @@ -977,13 +989,14 @@ Heroes® of Might and Magic® III HD is currently not supported! You can manually copy directories Maps, Data, and Mp3 from the original game directory to the VCMI data directory that you can see on top of this page - + Vous pouvez copier manuellement les répertoires Maps, Data et Mp3 du jeu original vers le répertoire de données VCMI visible en haut de cette page. If you own Heroes III on gog.com, you can download a backup offline installer from gog.com. VCMI will then import Heroes III data using the offline installer. Offline installer consists of two files: ".exe" and ".bin" - you must download both. - + Si vous possédez Heroes III sur gog.com, vous pouvez télécharger un installateur hors ligne de sauvegarde depuis gog.com. VCMI importera alors les données de Heroes III en utilisant l'installateur hors ligne. +L'installateur hors ligne est composé de deux fichiers : ".exe" et ".bin" - vous devez télécharger les deux. @@ -1014,7 +1027,7 @@ Offline installer consists of two files: ".exe" and ".bin" - Install mod that provides various interface improvements, such as a better interface for random maps and selectable actions in battles - + Installe un mod qui améliore l'interface, comme une meilleure interface pour les cartes aléatoires et des actions sélectionnables en bataille. @@ -1087,13 +1100,15 @@ Offline installer consists of two files: ".exe" and ".bin" - Heroes III: HD Edition files are not supported by VCMI. Please select the directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. - + Les fichiers de Heroes III: HD Edition ne sont pas pris en charge par VCMI. +Veuillez sélectionner le répertoire contenant Heroes III: Complete Edition ou Heroes III: Shadow of Death. Unknown or unsupported Heroes III version found. Please select the directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. - + Version inconnue ou non prise en charge de Heroes III détectée. +Veuillez sélectionner le répertoire contenant Heroes III: Complete Edition ou Heroes III: Shadow of Death. @@ -1103,12 +1118,12 @@ Please select the directory with Heroes III: Complete Edition or Heroes III: Sha You've provided a GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - + Vous avez fourni un installateur GOG Galaxy ! Ce fichier ne contient pas le jeu. Veuillez télécharger l'installateur de sauvegarde hors ligne ! Hash error! - + Erreur de hachage ! @@ -1124,7 +1139,8 @@ Please select the directory with Heroes III: Complete Edition or Heroes III: Sha Failed to detect valid Heroes III data in chosen directory. Please select the directory with installed Heroes III data. - + Impossible de détecter des données valides de Heroes III dans le répertoire choisi. +Veuillez sélectionner le répertoire contenant les données installées de Heroes III. @@ -1154,18 +1170,18 @@ Please select the directory with installed Heroes III data. Stream error while extracting files! error reason: - Erreur de flux lors de l'extraction des fichiers ! + Erreur de flux lors de l'extraction des fichiers ! Raison de l'erreur : Not a supported Inno Setup installer! - Programme d’installation Inno Setup non pris en charge ! + Ce n'est pas un installateur Inno Setup pris en charge ! VCMI was compiled without innoextract support, which is needed to extract exe files! - + VCMI a été compilé sans prise en charge de innoextract, ce qui est nécessaire pour extraire les fichiers exe ! @@ -1173,9 +1189,13 @@ Raison de l'erreur : Exe (%n bytes): %1 param is hash - - - + + Hachage SHA1 des fichiers fournis : +Exe (%n octet): +%1 + Hachage SHA1 des fichiers fournis : +Exe (%n octets): +%1 @@ -1184,9 +1204,13 @@ Exe (%n bytes): Bin (%n bytes): %1 param is hash - - - + + +Bin (%n octet): +%1 + +Bin (%n octets): +%1 @@ -1194,17 +1218,19 @@ Bin (%n bytes): Internal copy process failed. Enough space on device? %1 - + Échec du processus de copie interne. Espace suffisant sur l'appareil ? + +%1 Exe - + Exe Bin - + Bin @@ -1212,7 +1238,10 @@ Bin (%n bytes): %1 %2 - + Incohérence de langue ! +%1 + +%2 @@ -1220,14 +1249,19 @@ Bin (%n bytes): %1 %2 - + Un seul fichier connu ! Peut-être que les fichiers sont corrompus ? Veuillez télécharger à nouveau. +%1 + +%2 Unknown files! Maybe files are corrupted? Please download again. %1 - + Fichiers inconnus ! Peut-être que les fichiers sont corrompus ? Veuillez télécharger à nouveau. + +%1 @@ -1348,22 +1382,22 @@ Bin (%n bytes): Game - + Jeu Error starting executable - Erreur lors du démarrage de l'exécutable + Erreur au démarrage de l'exécutable Replace config file? - Remplacer le fichier de configuration ? + Remplacer le fichier de configuration ? Do you want to replace %1? - Voulez vous remplacer %1 ? + Voulez-vous remplacer %1 ? @@ -1384,79 +1418,79 @@ Bin (%n bytes): Can not install submod - Impossible d'installer le sous-mod + Impossible d'installer le sous-mod Mod is already installed - Le mod est déjà installé + Le mod est déjà installé Can not uninstall submod - Impossible de désinstaller le sousmod + Impossible de désinstaller le sous-mod Mod is not installed - Le mod n'est pas installé + Le mod n'est pas installé Mod is already enabled - Mod déjà activé + Le mod est déjà activé Mod must be installed first - Le mode doit d'abord être installé + Le mod doit d'abord être installé Mod is not compatible, please update VCMI and check the latest mod revisions - + Le mod n'est pas compatible, veuillez mettre à jour VCMI et vérifier les dernières révisions du mod Can not enable translation mod for a different language! - + Impossible d'activer le mod de traduction pour une langue différente ! Required mod %1 is missing - Le mod requis %1 est manquant + Le mod requis %1 est manquant Mod is already disabled - Mod déjà désactivé + Le mod est déjà désactivé Mod archive is missing - Archive du mod manquante + L'archive du mod est manquante Mod archive is invalid or corrupted - L'archive du mod est invalide ou corrompue + L'archive du mod est invalide ou corrompue Failed to extract mod data - Echec de l'extraction des données du mod + Échec de l'extraction des données du mod Mod data was not found - + Les données du mod n'ont pas été trouvées Mod is located in a protected directory, please remove it manually: - + Le mod est situé dans un répertoire protégé, veuillez le supprimer manuellement : @@ -1464,103 +1498,103 @@ Bin (%n bytes): Translation - Traduction + Traduction Town - Ville + Ville Test - Test + Test Templates - Modèles + Modèles Spells - Sorts + Sorts Music - Musique + Musique Maps - Cartes + Cartes Sounds - Sons + Sons Skills - Compétences + Compétences Other - Autre + Autre Objects - Objets + Objets Mechanics - Mécaniques + Mécanique Interface - Interface + Interface Heroes - Héros + Héros Graphical - Graphisme + Graphisme Expansion - Extension + Extension Creatures - Créatures + Créatures Compatibility - Compatibilité + Compatibilité Artifacts - Artefacts + Artéfacts AI - IA + IA @@ -1583,32 +1617,32 @@ Raison : %2 Import from Clipboard - + Importer depuis le Presse-papiers Rename Current Preset - + Renommer le Préréglage Actuel Create New Preset - + Créer un Nouveau Préréglage Export to Clipboard - + Exporter vers le Presse-papiers Delete Current Preset - + Supprimer le Préréglage Actuel Unsupported or corrupted game data detected! - + Données du jeu non prises en charge ou corrompues détectées ! @@ -1621,144 +1655,146 @@ Raison : %2 ? - + ? Install Translation - + Installer la Traduction No soundtrack detected! - + Aucune bande-son détectée ! Armaggedon's Blade campaigns are missing! - + Les campagnes d'Armageddon's Blade sont manquantes ! No video files detected! - + Aucun fichier vidéo détecté ! Activate Translation - + Activer la Traduction Import files - + Importer des fichiers Check For Updates - + Vérifier les mises à jour Go to Downloads Page - + Aller à la Page des Téléchargements Go to Changelog Page - + Aller à la Page du Journal des Modifications You are using the latest version - + Vous utilisez la dernière version Game Data Files - + Fichiers de Données du Jeu Mod Preset - + Préréglage du Mod Resume - + Reprendre Play - + Jouer Editor - + Éditeur Update %n mods - - - + + Mettre à jour %n mod + Mettre à jour %n mods Heroes Chronicles: %n/%1 installed - - - + + Heroes Chronicles : +%n/%1 installé + Heroes Chronicles : +%n/%1 installés Update to %1 available - + Mise à jour vers %1 disponible All supported files - Tous les fichiers supportés + Tous les fichiers pris en charge Maps - Cartes + Cartes Campaigns - Campagnes + Campagnes Configs - Configurations + Configurations Mods - Mods + Mods Gog files - + Fichiers Gog All files (*.*) - + Tous les fichiers (*.*) Select files (configs, mods, maps, campaigns, gog files) to install... - + Sélectionnez les fichiers (configurations, mods, cartes, campagnes, fichiers gog) à installer... @@ -1770,68 +1806,85 @@ Raison : %2 - VCMI mods in zip format (.zip) - VCMI configuration files (.json) - + Cette option vous permet d'importer des fichiers de données supplémentaires dans votre installation VCMI. Actuellement, les options suivantes sont prises en charge : + +- Cartes Heroes III (.h3m ou .vmap). +- Campagnes Heroes III (.h3c ou .vcmp). +- Heroes III Chronicles en utilisant l'installateur de sauvegarde hors ligne de GOG.com (.exe). +- Mods VCMI au format zip (.zip) +- Fichiers de configuration VCMI (.json) Your Heroes III version uses different language. VCMI provides translations of the game into various languages that you can use. Use this option to automatically install such translation to your language. - + Votre version de Heroes III utilise une langue différente. VCMI propose des traductions du jeu dans diverses langues que vous pouvez utiliser. Utilisez cette option pour installer automatiquement la traduction dans votre langue. Translation of Heroes III into your language is installed, but has been turned off. Use this option to enable it. - + La traduction de Heroes III dans votre langue est installée, mais elle a été désactivée. Utilisez cette option pour l'activer. A new version of some of the mods that you have installed is now available in mod repository. Use this option to automatically update all your mods to latest version. WARNING: In some cases, updated versions of mods may not be compatible with your existing saves. You may want to postpone mod update until you finish any of your ongoing games. - + Une nouvelle version de certains des mods que vous avez installés est maintenant disponible dans le référentiel de mods. Utilisez cette option pour mettre automatiquement à jour tous vos mods vers la dernière version. + +ATTENTION : Dans certains cas, les versions mises à jour des mods peuvent ne pas être compatibles avec vos sauvegardes existantes. Vous voudrez peut-être reporter la mise à jour des mods jusqu'à ce que vous ayez terminé vos parties en cours. If you own Heroes Chronicles on gog.com, you can use offline backup installers provided by gog to import Heroes Chronicles data into VCMI as custom campaigns. To import Heroes Chronicles, download offline backup installer of each chronicle that you wish to install, select 'Import files' option and select downloaded file. This will generate and install mod for VCMI that contains imported chronicles - + Si vous possédez Heroes Chronicles sur gog.com, vous pouvez utiliser les installateurs de sauvegarde hors ligne fournis par gog pour importer les données de Heroes Chronicles dans VCMI en tant que campagnes personnalisées. +Pour importer Heroes Chronicles, téléchargez l'installateur de sauvegarde hors ligne de chaque chronique que vous souhaitez installer, sélectionnez l'option 'Importer des fichiers' et sélectionnez le fichier téléchargé. Cela générera et installera un mod pour VCMI contenant les chroniques importées. VCMI has detected that Heroes III music files are missing from your installation. VCMI will run, but in-game music will not be available. To resolve this problem, please copy missing mp3 files from Heroes III to VCMI data files directory manually or reinstall VCMI and re-import Heroes III data files - + VCMI a détecté que les fichiers de musique de Heroes III sont manquants dans votre installation. VCMI fonctionnera, mais la musique en jeu ne sera pas disponible. + +Pour résoudre ce problème, veuillez copier manuellement les fichiers mp3 manquants de Heroes III vers le répertoire des fichiers de données VCMI ou réinstallez VCMI et réimportez les fichiers de données de Heroes III. VCMI has detected that Heroes III video files are missing from your installation. VCMI will run, but in-game cutscenes will not be available. To resolve this problem, please copy VIDEO.VID file from Heroes III to VCMI data files directory manually or reinstall VCMI and re-import Heroes III data files - + VCMI a détecté que les fichiers vidéo de Heroes III sont manquants dans votre installation. VCMI fonctionnera, mais les cinématiques du jeu ne seront pas disponibles. + +Pour résoudre ce problème, veuillez copier manuellement le fichier VIDEO.VID de Heroes III vers le répertoire des fichiers de données VCMI ou réinstallez VCMI et réimportez les fichiers de données de Heroes III. VCMI has detected that some of Heroes III data files are missing from your installation. You may attempt to run VCMI, but game may not work as expected or crash. To resolve this problem, please reinstall game and reimport data files using supported version of Heroes III. VCMI requires Heroes III: Shadow of Death or Complete Edition to run, which you can get (for example) from gog.com - + VCMI a détecté que certains fichiers de données de Heroes III sont manquants dans votre installation. Vous pouvez tenter d'exécuter VCMI, mais le jeu peut ne pas fonctionner comme prévu ou planter. + +Pour résoudre ce problème, veuillez réinstaller le jeu et réimporter les fichiers de données en utilisant une version prise en charge de Heroes III. VCMI nécessite Heroes III : Shadow of Death ou Complete Edition pour fonctionner, que vous pouvez obtenir (par exemple) sur gog.com. VCMI has detected that some of Heroes III: Armageddon's Blade data files are missing from your installation. VCMI will work, but Armageddon's Blade campaigns will not be available. To resolve this problem, please copy missing data files from Heroes III to VCMI data files directory manually or reinstall VCMI and re-import Heroes III data files - + VCMI a détecté que certains fichiers de données de Heroes III: Armageddon's Blade sont manquants dans votre installation. VCMI fonctionnera, mais les campagnes d'Armageddon's Blade ne seront pas disponibles. + +Pour résoudre ce problème, veuillez copier manuellement les fichiers de données manquants de Heroes III vers le répertoire des fichiers de données VCMI ou réinstallez VCMI et réimportez les fichiers de données de Heroes III. Enter preset name: - + Entrez le nom du préréglage : Rename preset '%1' to: - + Renommer le préréglage '%1' en : @@ -1859,7 +1912,7 @@ To resolve this problem, please copy missing data files from Heroes III to VCMI Cannot read JSON from URL or incorrect JSON data - + Impossible de lire le JSON depuis l'URL ou les données JSON sont incorrectes. - + \ No newline at end of file diff --git a/launcher/translation/hungarian.ts b/launcher/translation/hungarian.ts index 423ed872b..aaca54a9c 100644 --- a/launcher/translation/hungarian.ts +++ b/launcher/translation/hungarian.ts @@ -307,17 +307,20 @@ Context menu - + AI-generated, needs review by native speaker; delete this comment afterwards + Kontextus menü Open directory - + AI-generated, needs review by native speaker; delete this comment afterwards + Könyvtár megnyitása Open repository - + AI-generated, needs review by native speaker; delete this comment afterwards + Tároló megnyitása @@ -497,7 +500,8 @@ Sikeresen letöltött telepítés? Allow portrait mode - + AI-generated, needs review by native speaker; delete this comment afterwards + Álló mód engedélyezése diff --git a/launcher/translation/italian.ts b/launcher/translation/italian.ts index c97418ac2..cf142760d 100644 --- a/launcher/translation/italian.ts +++ b/launcher/translation/italian.ts @@ -307,17 +307,20 @@ Context menu - + AI-generated, needs review by native speaker; delete this comment afterwards + Menu contestuale Open directory - + AI-generated, needs review by native speaker; delete this comment afterwards + Apri cartella Open repository - + AI-generated, needs review by native speaker; delete this comment afterwards + Apri repository @@ -493,7 +496,8 @@ Installazione scaricata con successo? Allow portrait mode - + AI-generated, needs review by native speaker; delete this comment afterwards + Consenti modalità verticale diff --git a/launcher/translation/polish.ts b/launcher/translation/polish.ts index 711fb8d08..92e5c94d2 100644 --- a/launcher/translation/polish.ts +++ b/launcher/translation/polish.ts @@ -307,17 +307,17 @@ Context menu - + Menu kontekstowe Open directory - + Otwórz katalog Open repository - + Otwórz repozytorium @@ -502,7 +502,7 @@ Zainstalować pomyślnie pobrane? Allow portrait mode - + Zezwól na tryb portretowy @@ -1357,7 +1357,7 @@ Bin (%n bytes): Auto (%1) - + Auto (%1) @@ -1673,12 +1673,12 @@ Powód: %2 Armaggedon's Blade campaigns are missing! - + Brak kampanii Ostrze Armagedonu! No video files detected! - + Nie wykryto plików wideo! diff --git a/launcher/translation/portuguese.ts b/launcher/translation/portuguese.ts index 6f511602a..0b0fb3611 100644 --- a/launcher/translation/portuguese.ts +++ b/launcher/translation/portuguese.ts @@ -307,17 +307,20 @@ Context menu - + AI-generated, needs review by native speaker; delete this comment afterwards + Menu de contexto Open directory - + AI-generated, needs review by native speaker; delete this comment afterwards + Abrir diretório Open repository - + AI-generated, needs review by native speaker; delete this comment afterwards + Abrir repositório @@ -497,7 +500,8 @@ O download da instalação foi bem-sucedido? Allow portrait mode - + AI-generated, needs review by native speaker; delete this comment afterwards + Permitir modo retrato diff --git a/launcher/translation/russian.ts b/launcher/translation/russian.ts index acc10b642..e1988c7df 100644 --- a/launcher/translation/russian.ts +++ b/launcher/translation/russian.ts @@ -307,17 +307,17 @@ Context menu - + Контекстное меню Open directory - + Открыть каталог Open repository - + Открыть репозиторий @@ -532,7 +532,7 @@ Install successfully downloaded? Allow portrait mode - + Разрешить портретный режим @@ -547,7 +547,7 @@ Install successfully downloaded? Handle back as right mouse button - + Обрабатывать кнопку «Назад» как правую кнопку мыши @@ -940,12 +940,12 @@ Fullscreen Exclusive Mode - the game will cover the entirety of your screen and Install compatible version of "Horn of the Abyss", a fan-made Heroes III expansion ported by the VCMI team - Установить совместимую версию Horn of the Abyss: фанатского дополнения к Героям III (портированную командой VCMI) + Установить совместимую версию Рога Бездны: фанатского дополнения к Героям III (портированного командой VCMI) Install compatible version of "In The Wake of Gods", a fan-made Heroes III expansion - Установить совместимую версию In The Wake of Gods: фанатского дополнения к Героям III (портированную командой VCMI) + Установить совместимую версию Во Имя Богов: фанатского дополнения к Героям III (портированного командой VCMI) @@ -1036,7 +1036,7 @@ Offline installer consists of two files: ".exe" and ".bin" - Horn of the Abyss - + Рог Бездны @@ -1046,7 +1046,7 @@ Offline installer consists of two files: ".exe" and ".bin" - In The Wake of Gods - + Во Имя Богов @@ -1188,7 +1188,7 @@ error reason: Exe (%n bytes): %1 param is hash - + SHA1 хэш предоставленных файлов: Exe (%n байт): %1 @@ -1202,7 +1202,7 @@ Exe (%n байт): Bin (%n bytes): %1 param is hash - + Bin (%n байт): %1 @@ -1384,7 +1384,7 @@ Bin (%n байт): Error starting executable - Ошибка запуска исполняемого файла + Ошибка запуска исполняемого файла @@ -1481,7 +1481,7 @@ Bin (%n байт): Mod data was not found - + Данные мода не найдены diff --git a/launcher/translation/spanish.ts b/launcher/translation/spanish.ts index 71bf608c5..d569c79c9 100644 --- a/launcher/translation/spanish.ts +++ b/launcher/translation/spanish.ts @@ -307,17 +307,20 @@ Context menu - + AI-generated, needs review by native speaker; delete this comment afterwards + Menú contextual Open directory - + AI-generated, needs review by native speaker; delete this comment afterwards + Abrir directorio Open repository - + AI-generated, needs review by native speaker; delete this comment afterwards + Abrir repositorio @@ -502,7 +505,8 @@ Instalar lo correctamente descargado? Allow portrait mode - + AI-generated, needs review by native speaker; delete this comment afterwards + Permitir modo retrato diff --git a/launcher/translation/vietnamese.ts b/launcher/translation/vietnamese.ts index 1d9527fe2..91a693ffc 100644 --- a/launcher/translation/vietnamese.ts +++ b/launcher/translation/vietnamese.ts @@ -307,17 +307,20 @@ Context menu - + AI-generated, needs review by native speaker; delete this comment afterwards + Menu ngữ cảnh Open directory - + AI-generated, needs review by native speaker; delete this comment afterwards + Mở thư mục Open repository - + AI-generated, needs review by native speaker; delete this comment afterwards + Mở kho lưu trữ @@ -347,7 +350,9 @@ Có lỗi sau: Install successfully downloaded? - +AI-generated, needs review by native speaker; delete this comment afterwards + +Cài đặt đã tải xuống thành công? @@ -438,17 +443,20 @@ Install successfully downloaded? Sticks Sensitivity - + AI-generated, needs review by native speaker; delete this comment afterwards + Độ nhạy cần điều khiển Automatic (Linear) - + AI-generated, needs review by native speaker; delete this comment afterwards + Tự động (Tuyến tính) Haptic Feedback - + AI-generated, needs review by native speaker; delete this comment afterwards + Phản hồi xúc giác @@ -475,17 +483,20 @@ Install successfully downloaded? xBRZ x2 - + AI-generated, needs review by native speaker; delete this comment afterwards + xBRZ x2 xBRZ x3 - + AI-generated, needs review by native speaker; delete this comment afterwards + xBRZ x3 xBRZ x4 - + AI-generated, needs review by native speaker; delete this comment afterwards + xBRZ x4 @@ -495,7 +506,8 @@ Install successfully downloaded? Allow portrait mode - + AI-generated, needs review by native speaker; delete this comment afterwards + Cho phép chế độ dọc @@ -510,7 +522,8 @@ Install successfully downloaded? Handle back as right mouse button - + AI-generated, needs review by native speaker; delete this comment afterwards + Xử lý nút quay lại như chuột phải @@ -567,17 +580,20 @@ Toàn màn hình riêng biệt - Sử dụng kích thước màn hình do bạn Use Relative Pointer Mode - + AI-generated, needs review by native speaker; delete this comment afterwards + Sử dụng chế độ con trỏ tương đối Nearest - + AI-generated, needs review by native speaker; delete this comment afterwards + Gần nhất Linear - + AI-generated, needs review by native speaker; delete this comment afterwards + Tuyến tính @@ -632,17 +648,20 @@ Toàn màn hình riêng biệt - Sử dụng kích thước màn hình do bạn Long Touch Duration - + AI-generated, needs review by native speaker; delete this comment afterwards + Thời gian nhấn giữ lâu Controller Click Tolerance - + AI-generated, needs review by native speaker; delete this comment afterwards + Độ nhạy nhấp chuột của bộ điều khiển Touch Tap Tolerance - + AI-generated, needs review by native speaker; delete this comment afterwards + Độ nhạy chạm màn hình @@ -692,12 +711,14 @@ Toàn màn hình riêng biệt - Sử dụng kích thước màn hình do bạn Mouse Click Tolerance - + AI-generated, needs review by native speaker; delete this comment afterwards + Độ nhạy nhấp chuột Sticks Acceleration - + AI-generated, needs review by native speaker; delete this comment afterwards + Gia tốc cần điều khiển @@ -767,7 +788,8 @@ Toàn màn hình riêng biệt - Sử dụng kích thước màn hình do bạn VSync - + AI-generated, needs review by native speaker; delete this comment afterwards + VSync @@ -841,12 +863,14 @@ Toàn màn hình riêng biệt - Sử dụng kích thước màn hình do bạn Heroes Chronicles - + AI-generated, needs review by native speaker; delete this comment afterwards + Heroes Chronicles Heroes Chronicles %1 - %2 - + AI-generated, needs review by native speaker; delete this comment afterwards + Heroes Chronicles %1 - %2 @@ -855,7 +879,8 @@ Toàn màn hình riêng biệt - Sử dụng kích thước màn hình do bạn %1 MiB - + AI-generated, needs review by native speaker; delete this comment afterwards + %1 MiB @@ -873,7 +898,8 @@ Toàn màn hình riêng biệt - Sử dụng kích thước màn hình do bạn Mods Preset - + AI-generated, needs review by native speaker; delete this comment afterwards + Thiết lập mod @@ -1212,12 +1238,14 @@ Bin (%n bytes): Exe - + AI-generated, needs review by native speaker; delete this comment afterwards + Exe Bin - + AI-generated, needs review by native speaker; delete this comment afterwards + Bin @@ -1364,7 +1392,8 @@ Bin (%n bytes): Game - + AI-generated, needs review by native speaker; delete this comment afterwards + Trò chơi @@ -1582,7 +1611,8 @@ Bin (%n bytes): AI - + AI-generated, needs review by native speaker; delete this comment afterwards + AI @@ -1605,27 +1635,32 @@ Có thể do lỗi: %2 Import from Clipboard - + AI-generated, needs review by native speaker; delete this comment afterwards + Nhập từ bảng tạm Rename Current Preset - + AI-generated, needs review by native speaker; delete this comment afterwards + Đổi tên thiết lập hiện tại Create New Preset - + AI-generated, needs review by native speaker; delete this comment afterwards + Tạo thiết lập mới Export to Clipboard - + AI-generated, needs review by native speaker; delete this comment afterwards + Xuất ra bảng tạm Delete Current Preset - + AI-generated, needs review by native speaker; delete this comment afterwards + Xóa thiết lập hiện tại @@ -1643,7 +1678,8 @@ Có thể do lỗi: %2 ? - + AI-generated, needs review by native speaker; delete this comment afterwards + ? @@ -1703,12 +1739,14 @@ Có thể do lỗi: %2 Mod Preset - + AI-generated, needs review by native speaker; delete this comment afterwards + Thiết lập mod Resume - + AI-generated, needs review by native speaker; delete this comment afterwards + Tiếp tục @@ -1749,17 +1787,20 @@ cài đặt %n/%1 Maps + AI-generated, needs review by native speaker; delete this comment afterwards Bản đồ Campaigns - + AI-generated, needs review by native speaker; delete this comment afterwards + Chiến dịch Configs - + AI-generated, needs review by native speaker; delete this comment afterwards + Cấu hình @@ -1769,7 +1810,8 @@ cài đặt %n/%1 Gog files - + AI-generated, needs review by native speaker; delete this comment afterwards + Tệp Gog @@ -1791,68 +1833,96 @@ cài đặt %n/%1 - VCMI mods in zip format (.zip) - VCMI configuration files (.json) - +AI-generated, needs review by native speaker; delete this comment afterwards + Tùy chọn này cho phép bạn nhập thêm tệp dữ liệu vào cài đặt VCMI của bạn. Hiện tại, các tùy chọn sau được hỗ trợ: + +- Bản đồ Heroes III (.h3m hoặc .vmap). +- Chiến dịch Heroes III (.h3c hoặc .vcmp). +- Heroes III Chronicles bằng trình cài đặt sao lưu ngoại tuyến từ GOG.com (.exe). +- Mod VCMI ở định dạng zip (.zip). +- Tệp cấu hình VCMI (.json). Your Heroes III version uses different language. VCMI provides translations of the game into various languages that you can use. Use this option to automatically install such translation to your language. - + AI-generated, needs review by native speaker; delete this comment afterwards + Phiên bản Heroes III của bạn sử dụng ngôn ngữ khác. VCMI cung cấp bản dịch trò chơi sang nhiều ngôn ngữ mà bạn có thể sử dụng. Sử dụng tùy chọn này để tự động cài đặt bản dịch sang ngôn ngữ của bạn. Translation of Heroes III into your language is installed, but has been turned off. Use this option to enable it. - + AI-generated, needs review by native speaker; delete this comment afterwards + Bản dịch Heroes III sang ngôn ngữ của bạn đã được cài đặt nhưng đang bị tắt. Sử dụng tùy chọn này để bật nó. A new version of some of the mods that you have installed is now available in mod repository. Use this option to automatically update all your mods to latest version. WARNING: In some cases, updated versions of mods may not be compatible with your existing saves. You may want to postpone mod update until you finish any of your ongoing games. - +AI-generated, needs review by native speaker; delete this comment afterwards + Một phiên bản mới của một số mod mà bạn đã cài đặt hiện có sẵn trong kho lưu trữ mod. Sử dụng tùy chọn này để tự động cập nhật tất cả các mod của bạn lên phiên bản mới nhất. + +CẢNH BÁO: Trong một số trường hợp, các phiên bản cập nhật của mod có thể không tương thích với các bản lưu hiện có của bạn. Bạn có thể muốn hoãn cập nhật mod cho đến khi hoàn thành trò chơi hiện tại của mình. If you own Heroes Chronicles on gog.com, you can use offline backup installers provided by gog to import Heroes Chronicles data into VCMI as custom campaigns. To import Heroes Chronicles, download offline backup installer of each chronicle that you wish to install, select 'Import files' option and select downloaded file. This will generate and install mod for VCMI that contains imported chronicles - +AI-generated, needs review by native speaker; delete this comment afterwards + Nếu bạn sở hữu Heroes Chronicles trên GOG.com, bạn có thể sử dụng trình cài đặt sao lưu ngoại tuyến do GOG cung cấp để nhập dữ liệu Heroes Chronicles vào VCMI dưới dạng chiến dịch tùy chỉnh. +Để nhập Heroes Chronicles, hãy tải xuống trình cài đặt sao lưu ngoại tuyến của từng phần Chronicles mà bạn muốn cài đặt, chọn tùy chọn 'Nhập tệp' và chọn tệp đã tải xuống. Điều này sẽ tạo và cài đặt mod cho VCMI chứa các Chronicles đã nhập. VCMI has detected that Heroes III music files are missing from your installation. VCMI will run, but in-game music will not be available. To resolve this problem, please copy missing mp3 files from Heroes III to VCMI data files directory manually or reinstall VCMI and re-import Heroes III data files - +AI-generated, needs review by native speaker; delete this comment afterwards + VCMI đã phát hiện rằng các tệp nhạc Heroes III bị thiếu trong cài đặt của bạn. VCMI sẽ chạy, nhưng nhạc trong trò chơi sẽ không khả dụng. + +Để giải quyết vấn đề này, vui lòng sao chép các tệp mp3 bị thiếu từ Heroes III vào thư mục dữ liệu VCMI theo cách thủ công hoặc cài đặt lại VCMI và nhập lại các tệp dữ liệu Heroes III. VCMI has detected that Heroes III video files are missing from your installation. VCMI will run, but in-game cutscenes will not be available. To resolve this problem, please copy VIDEO.VID file from Heroes III to VCMI data files directory manually or reinstall VCMI and re-import Heroes III data files - +AI-generated, needs review by native speaker; delete this comment afterwards + VCMI đã phát hiện rằng các tệp video Heroes III bị thiếu trong cài đặt của bạn. VCMI sẽ chạy, nhưng các cảnh cắt trong trò chơi sẽ không khả dụng. + +Để giải quyết vấn đề này, vui lòng sao chép tệp VIDEO.VID từ Heroes III vào thư mục dữ liệu VCMI theo cách thủ công hoặc cài đặt lại VCMI và nhập lại các tệp dữ liệu Heroes III. VCMI has detected that some of Heroes III data files are missing from your installation. You may attempt to run VCMI, but game may not work as expected or crash. To resolve this problem, please reinstall game and reimport data files using supported version of Heroes III. VCMI requires Heroes III: Shadow of Death or Complete Edition to run, which you can get (for example) from gog.com - +AI-generated, needs review by native speaker; delete this comment afterwards + VCMI đã phát hiện rằng một số tệp dữ liệu của Heroes III bị thiếu trong cài đặt của bạn. Bạn có thể cố gắng chạy VCMI, nhưng trò chơi có thể không hoạt động như mong đợi hoặc bị lỗi. + +Để giải quyết vấn đề này, vui lòng cài đặt lại trò chơi và nhập lại các tệp dữ liệu bằng phiên bản Heroes III được hỗ trợ. VCMI yêu cầu Heroes III: Shadow of Death hoặc Complete Edition để chạy, mà bạn có thể mua (ví dụ) từ GOG.com. VCMI has detected that some of Heroes III: Armageddon's Blade data files are missing from your installation. VCMI will work, but Armageddon's Blade campaigns will not be available. To resolve this problem, please copy missing data files from Heroes III to VCMI data files directory manually or reinstall VCMI and re-import Heroes III data files - +AI-generated, needs review by native speaker; delete this comment afterwards + VCMI đã phát hiện rằng một số tệp dữ liệu của Heroes III: Armageddon's Blade bị thiếu trong cài đặt của bạn. VCMI sẽ hoạt động, nhưng các chiến dịch của Armageddon's Blade sẽ không khả dụng. + +Để giải quyết vấn đề này, vui lòng sao chép các tệp dữ liệu bị thiếu từ Heroes III vào thư mục dữ liệu VCMI theo cách thủ công hoặc cài đặt lại VCMI và nhập lại các tệp dữ liệu Heroes III. Enter preset name: - + AI-generated, needs review by native speaker; delete this comment afterwards + Nhập tên thiết lập: Rename preset '%1' to: - + AI-generated, needs review by native speaker; delete this comment afterwards + Đổi tên thiết lập '%1' thành: diff --git a/lib/CConfigHandler.cpp b/lib/CConfigHandler.cpp index c89c125ee..4fbfc57fe 100644 --- a/lib/CConfigHandler.cpp +++ b/lib/CConfigHandler.cpp @@ -55,6 +55,13 @@ SettingsStorage::SettingsStorage(): { } +SettingsStorage::~SettingsStorage() +{ + // hack for possible crash due to static destruction order (setting storage can be destroyed before all listeners have died) + for(SettingsListener * listener : listeners) + listener->terminate(); +} + void SettingsStorage::init(const std::string & dataFilename, const std::string & schema) { this->dataFilename = dataFilename; @@ -132,9 +139,15 @@ SettingsListener::SettingsListener(const SettingsListener &sl): parent.listeners.insert(this); } +void SettingsListener::terminate() +{ + wasTerminated = true; +} + SettingsListener::~SettingsListener() { - parent.listeners.erase(this); + if (!wasTerminated) + parent.listeners.erase(this); } void SettingsListener::nodeInvalidated(const std::vector &changedPath) diff --git a/lib/CConfigHandler.h b/lib/CConfigHandler.h index 915863a52..4822db41c 100644 --- a/lib/CConfigHandler.h +++ b/lib/CConfigHandler.h @@ -48,6 +48,7 @@ class DLL_LINKAGE SettingsStorage public: // Initialize config structure SettingsStorage(); + ~SettingsStorage(); void init(const std::string & dataFilename, const std::string & schema); // Get write access to config node at path @@ -73,11 +74,15 @@ class DLL_LINKAGE SettingsListener // Callback std::function callback; + // hack for crash due to static destruction order + bool wasTerminated = false; + SettingsListener(SettingsStorage & _parent, std::vector _path); // Executes callback if changedpath begins with path void nodeInvalidated(const std::vector & changedPath); + void terminate(); public: SettingsListener(const SettingsListener &sl); ~SettingsListener(); diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 2dbdadebf..b399ab4fa 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1623,13 +1623,19 @@ void RebalanceStacks::applyGs(CGameState *gs) { if(auto dstArt = dstStack->getArt(ArtifactPosition::CREATURE_SLOT)) { - auto dstSlot = ArtifactUtils::getArtBackpackPosition(srcHero, dstArt->getTypeId()); - if(srcHero && dstSlot != ArtifactPosition::PRE_FIRST) + bool artifactIsLost = true; + + if(srcHero) { - gs->getMap().moveArtifactInstance(*dstStack, ArtifactPosition::CREATURE_SLOT, *srcHero, dstSlot); + auto dstSlot = ArtifactUtils::getArtBackpackPosition(srcHero, dstArt->getTypeId()); + if (dstSlot != ArtifactPosition::PRE_FIRST) + { + gs->getMap().moveArtifactInstance(*dstStack, ArtifactPosition::CREATURE_SLOT, *srcHero, dstSlot); + artifactIsLost = false; + } } - //else - artifact can be lost :/ - else + + if (artifactIsLost) { BulkEraseArtifacts ea; ea.artHolder = dstHero->id; diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index f3e58e4d6..0bccec9f2 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -70,6 +70,15 @@ void ApplyGhNetPackVisitor::visitMoveHero(MoveHero & pack) result = false; return; } + + // player got some query he has to reply to first for example, from triggered event + // ignore remaining path (if any), but handle this as success - since at least part of path was legal & was applied + auto query = gh.queries->topQuery(pack.player); + if (query && query->blocksPack(&pack)) + { + result = true; + return; + } } result = true;