diff --git a/.github/workflows/github.yml b/.github/workflows/github.yml index 5b2dfe7a6..99dcbd8b7 100644 --- a/.github/workflows/github.yml +++ b/.github/workflows/github.yml @@ -352,7 +352,7 @@ jobs: run: | find . -path ./.git -prune -o -path ./AI/FuzzyLite -prune -o -path ./test/googletest \ -o -path ./osx -prune -o -type f \ - -not -name '*.png' -and -not -name '*.vcxproj*' -and -not -name '*.props' -and -not -name '*.wav' -and -not -name '*.webm' -and -not -name '*.ico' -and -not -name '*.bat' -print0 | \ + -not -name '*.png' -and -not -name '*.ttf' -and -not -name '*.wav' -and -not -name '*.webm' -and -not -name '*.ico' -and -not -name '*.bat' -print0 | \ { ! xargs -0 grep -l -z -P '\r\n'; } - name: Validate JSON diff --git a/Mods/vcmi/Data/NotoSans-Medium.ttf b/Mods/vcmi/Data/NotoSans-Medium.ttf new file mode 100644 index 000000000..4c5069f50 Binary files /dev/null and b/Mods/vcmi/Data/NotoSans-Medium.ttf differ diff --git a/Mods/vcmi/Data/NotoSerif-Black.ttf b/Mods/vcmi/Data/NotoSerif-Black.ttf new file mode 100644 index 000000000..cc9ebad47 Binary files /dev/null and b/Mods/vcmi/Data/NotoSerif-Black.ttf differ diff --git a/Mods/vcmi/Data/NotoSerif-Bold.ttf b/Mods/vcmi/Data/NotoSerif-Bold.ttf new file mode 100644 index 000000000..13eb2a0a8 Binary files /dev/null and b/Mods/vcmi/Data/NotoSerif-Bold.ttf differ diff --git a/Mods/vcmi/Data/NotoSerif-Medium.ttf b/Mods/vcmi/Data/NotoSerif-Medium.ttf new file mode 100644 index 000000000..ae77bd049 Binary files /dev/null and b/Mods/vcmi/Data/NotoSerif-Medium.ttf differ diff --git a/Mods/vcmi/config/vcmi/chinese.json b/Mods/vcmi/config/vcmi/chinese.json index 791876f71..9ac1d1952 100644 --- a/Mods/vcmi/config/vcmi/chinese.json +++ b/Mods/vcmi/config/vcmi/chinese.json @@ -12,6 +12,7 @@ "vcmi.adventureMap.monsterThreat.levels.9" : "压倒性的", "vcmi.adventureMap.monsterThreat.levels.10" : "致命的", "vcmi.adventureMap.monsterThreat.levels.11" : "无法取胜", + "vcmi.adventureMap.monsterLevel" : "\n\n%TOWN%LEVEL级生物", "vcmi.adventureMap.confirmRestartGame" : "你想要重新开始游戏吗?", "vcmi.adventureMap.noTownWithMarket" : "没有足够的市场。", @@ -20,6 +21,7 @@ "vcmi.adventureMap.playerAttacked" : "玩家遭受攻击: %s", "vcmi.adventureMap.moveCostDetails" : "移动点数 - 花费: %TURNS 轮 + %POINTS 点移动力, 剩余移动力: %REMAINING", "vcmi.adventureMap.moveCostDetailsNoTurns" : "移动点数 - 花费: %POINTS 点移动力, 剩余移动力: %REMAINING", + "vcmi.adventureMap.movementPointsHeroInfo" : "(移动点数: %REMAINING / %POINTS)", "vcmi.adventureMap.replayOpponentTurnNotImplemented" : "抱歉,重放对手行动功能目前暂未实现!", "vcmi.capitalColors.0" : "红色", @@ -236,6 +238,8 @@ "vcmi.adventureOptions.infoBarCreatureManagement.help" : "{信息面板生物管理}\n\n允许在信息面板中重新排列生物,而不是在默认组件之间循环。", "vcmi.adventureOptions.leftButtonDrag.hover" : "左键拖动地图", "vcmi.adventureOptions.leftButtonDrag.help" : "{左键拖动地图}\n\n启用后,按住左键移动鼠标将拖动冒险地图视图。", + "vcmi.adventureOptions.rightButtonDrag.hover" : "右键拖动地图", + "vcmi.adventureOptions.rightButtonDrag.help" : "{右键拖动地图}\n\n启用后,按住右键移动鼠标将拖动冒险地图视图。", "vcmi.adventureOptions.smoothDragging.hover" : "平滑地图拖动", "vcmi.adventureOptions.smoothDragging.help" : "{平滑地图拖动}\n\n启用后,地图拖动会产生柔和的羽化效果。", "vcmi.adventureOptions.skipAdventureMapAnimations.hover" : "关闭淡入淡出特效", @@ -662,5 +666,7 @@ "core.bonus.WIDE_BREATH.name": "弧形焰息", "core.bonus.WIDE_BREATH.description": "大范围喷吐攻击(目标左右以及后方共6格)", "core.bonus.DISINTEGRATE.name": "解体", - "core.bonus.DISINTEGRATE.description": "死亡后不会留下尸体" + "core.bonus.DISINTEGRATE.description": "死亡后不会留下尸体", + "core.bonus.INVINCIBLE.name": "无敌", + "core.bonus.INVINCIBLE.description": "不受任何效果影响" } diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1f377e57a..991aba090 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -95,10 +95,10 @@ set(vcmiclientcommon_SRCS render/ImageLocator.cpp renderSDL/CBitmapFont.cpp - renderSDL/CBitmapHanFont.cpp renderSDL/CTrueTypeFont.cpp renderSDL/CursorHardware.cpp renderSDL/CursorSoftware.cpp + renderSDL/FontChain.cpp renderSDL/ImageScaled.cpp renderSDL/RenderHandler.cpp renderSDL/SDLImage.cpp @@ -303,10 +303,10 @@ set(vcmiclientcommon_HEADERS render/IScreenHandler.h renderSDL/CBitmapFont.h - renderSDL/CBitmapHanFont.h renderSDL/CTrueTypeFont.h renderSDL/CursorHardware.h renderSDL/CursorSoftware.h + renderSDL/FontChain.h renderSDL/ImageScaled.h renderSDL/RenderHandler.h renderSDL/SDLImage.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 1100b26c8..802364f6b 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -930,7 +930,7 @@ void CPlayerInterface::battleAttack(const BattleID & battleID, const BattleAttac info.secondaryDefender.push_back(cb->getBattle(battleID)->battleGetStackByID(elem.stackAttacked)); } } - assert(info.defender != nullptr); + assert(info.defender != nullptr || (info.spellEffect != SpellID::NONE && info.indirectAttack)); assert(info.attacker != nullptr); battleInt->stackAttacking(info); diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index 8635f24d1..434ec46cb 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -175,6 +175,18 @@ void BattleActionsController::enterCreatureCastingMode() if (!owner.stacksController->getActiveStack()) return; + if(owner.getBattle()->battleCanTargetEmptyHex(owner.stacksController->getActiveStack())) + { + auto actionFilterPredicate = [](const PossiblePlayerBattleAction x) + { + return x.get() != PossiblePlayerBattleAction::SHOOT; + }; + + vstd::erase_if(possibleActions, actionFilterPredicate); + GH.fakeMouseMove(); + return; + } + if (!isActiveStackSpellcaster()) return; @@ -263,6 +275,9 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac return 2; break; case PossiblePlayerBattleAction::SHOOT: + if(targetStack == nullptr || targetStack->unitSide() == stack->unitSide()) + return 100; //bottom priority + return 4; break; case PossiblePlayerBattleAction::ATTACK_AND_RETURN: @@ -356,6 +371,12 @@ const CSpell * BattleActionsController::getStackSpellToCast(BattleHex hoveredHex auto action = selectAction(hoveredHex); + if(owner.stacksController->getActiveStack()->hasBonusOfType(BonusType::SPELL_LIKE_ATTACK)) + { + auto bonus = owner.stacksController->getActiveStack()->getBonus(Selector::type()(BonusType::SPELL_LIKE_ATTACK)); + return bonus->subtype.as().toSpell(); + } + if (action.spell() == SpellID::NONE) return nullptr; @@ -514,6 +535,13 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle case PossiblePlayerBattleAction::SHOOT: { + if(targetStack == nullptr) //should be true only for spell-like attack + { + auto spellLikeAttackBonus = owner.stacksController->getActiveStack()->getBonus(Selector::type()(BonusType::SPELL_LIKE_ATTACK)); + assert(spellLikeAttackBonus != nullptr); + return boost::str(boost::format(CGI->generaltexth->allTexts[26]) % spellLikeAttackBonus->subtype.as().toSpell()->getNameTranslated()); + } + const auto * shooter = owner.stacksController->getActiveStack(); DamageEstimation retaliation; @@ -625,7 +653,20 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B return false; case PossiblePlayerBattleAction::SHOOT: - return owner.getBattle()->battleCanShoot(owner.stacksController->getActiveStack(), targetHex); + { + auto currentStack = owner.stacksController->getActiveStack(); + if(!owner.getBattle()->battleCanShoot(currentStack, targetHex)) + return false; + + if(targetStack == nullptr && owner.getBattle()->battleCanTargetEmptyHex(currentStack)) + { + auto spellLikeAttackBonus = currentStack->getBonus(Selector::type()(BonusType::SPELL_LIKE_ATTACK)); + const CSpell * spellDataToCheck = spellLikeAttackBonus->subtype.as().toSpell(); + return isCastingPossibleHere(spellDataToCheck, nullptr, targetHex); + } + + return true; + } case PossiblePlayerBattleAction::NO_LOCATION: return false; @@ -771,7 +812,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B } } - if (!spellcastingModeActive()) + if (!heroSpellcastingModeActive()) { if (action.spell().hasValue()) { @@ -1018,14 +1059,9 @@ void BattleActionsController::activateStack() void BattleActionsController::onHexRightClicked(BattleHex clickedHex) { - auto spellcastActionPredicate = [](PossiblePlayerBattleAction & action) - { - return action.spellcast(); - }; + bool isCurrentStackInSpellcastMode = creatureSpellcastingModeActive(); - bool isCurrentStackInSpellcastMode = !possibleActions.empty() && std::all_of(possibleActions.begin(), possibleActions.end(), spellcastActionPredicate); - - if (spellcastingModeActive() || isCurrentStackInSpellcastMode) + if (heroSpellcastingModeActive() || isCurrentStackInSpellcastMode) { endCastingSpell(); CRClickPopup::createAndPush(CGI->generaltexth->translate("core.genrltxt.731")); // spell cancelled @@ -1044,11 +1080,21 @@ void BattleActionsController::onHexRightClicked(BattleHex clickedHex) owner.defendingHero->heroRightClicked(); } -bool BattleActionsController::spellcastingModeActive() const +bool BattleActionsController::heroSpellcastingModeActive() const { return heroSpellToCast != nullptr; } +bool BattleActionsController::creatureSpellcastingModeActive() const +{ + auto spellcastModePredicate = [](const PossiblePlayerBattleAction & action) + { + return action.spellcast() || action.get() == PossiblePlayerBattleAction::SHOOT; //for hotkey-eligible SPELL_LIKE_ATTACK creature should have only SHOOT action + }; + + return !possibleActions.empty() && std::all_of(possibleActions.begin(), possibleActions.end(), spellcastModePredicate); +} + bool BattleActionsController::currentActionSpellcasting(BattleHex hoveredHex) { if (heroSpellToCast) diff --git a/client/battle/BattleActionsController.h b/client/battle/BattleActionsController.h index 409f3172a..74db750e1 100644 --- a/client/battle/BattleActionsController.h +++ b/client/battle/BattleActionsController.h @@ -82,8 +82,10 @@ public: /// initialize list of potential actions for new active stack void activateStack(); - /// returns true if UI is currently in target selection mode - bool spellcastingModeActive() const; + /// returns true if UI is currently in hero spell target selection mode + bool heroSpellcastingModeActive() const; + /// returns true if UI is currently in "F" hotkey creature spell target selection mode + bool creatureSpellcastingModeActive() const; /// returns true if one of the following is true: /// - we are casting spell by hero diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 409a95bab..95ab1c349 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -566,7 +566,9 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas) calculateRangeLimitAndHighlightImages(shootingRangeDistance, shootingRangeLimitImages, shootingRangeLimitHexes, shootingRangeLimitHexesHighlights); } - bool useSpellRangeForMouse = hoveredHex != BattleHex::INVALID && owner.actionsController->currentActionSpellcasting(getHoveredHex()); + bool useSpellRangeForMouse = hoveredHex != BattleHex::INVALID + && (owner.actionsController->currentActionSpellcasting(getHoveredHex()) + || owner.actionsController->creatureSpellcastingModeActive()); //at least shooting with SPELL_LIKE_ATTACK can operate in spellcasting mode without being actual spellcast bool useMoveRangeForMouse = !hoveredMoveHexes.empty() || !settings["battle"]["mouseShadow"].Bool(); const auto & hoveredMouseHexes = useSpellRangeForMouse ? hoveredSpellHexes : ( useMoveRangeForMouse ? hoveredMoveHexes : hoveredMouseHex); diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 1f9413f49..919118407 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -112,9 +112,10 @@ std::vector BattleConsole::splitText(const std::string &text) boost::split(lines, text, boost::is_any_of("\n")); + const auto & font = GH.renderHandler().loadFont(FONT_SMALL); for(const auto & line : lines) { - if (graphics->fonts[FONT_SMALL]->getStringWidth(text) < pos.w) + if (font->getStringWidth(text) < pos.w) { output.push_back(line); } @@ -327,7 +328,7 @@ void BattleHero::setPhase(EHeroAnimType newPhase) void BattleHero::heroLeftClicked() { - if(owner.actionsController->spellcastingModeActive()) //we are casting a spell + if(owner.actionsController->heroSpellcastingModeActive()) //we are casting a spell return; if(!hero || !owner.makingTurn()) @@ -1098,7 +1099,8 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn, std:: if(currentTurn && !owner->embedded) { std::string tmp = std::to_string(*currentTurn); - int len = graphics->fonts[FONT_SMALL]->getStringWidth(tmp); + const auto & font = GH.renderHandler().loadFont(FONT_SMALL); + int len = font->getStringWidth(tmp); roundRect->pos.w = len + 6; round->setText(tmp); } diff --git a/client/battle/BattleOverlayLogVisualizer.cpp b/client/battle/BattleOverlayLogVisualizer.cpp index 101da5fd1..fd7af4720 100644 --- a/client/battle/BattleOverlayLogVisualizer.cpp +++ b/client/battle/BattleOverlayLogVisualizer.cpp @@ -17,7 +17,9 @@ #include "../render/Colors.h" #include "../render/EFont.h" #include "../render/IFont.h" +#include "../render/IRenderHandler.h" #include "../gui/TextAlignment.h" +#include "../gui/CGuiHandler.h" #include "../render/Graphics.h" BattleOverlayLogVisualizer::BattleOverlayLogVisualizer( @@ -30,7 +32,8 @@ BattleOverlayLogVisualizer::BattleOverlayLogVisualizer( void BattleOverlayLogVisualizer::drawText(BattleHex hex, int lineNumber, const std::string & text) { Point offset = owner.fieldController->hexPositionLocal(hex).topLeft() + Point(20, 20); - int h = graphics->fonts[EFonts::FONT_TINY]->getLineHeight(); + const auto & font = GH.renderHandler().loadFont(FONT_TINY); + int h = font->getLineHeight(); offset.y += h * lineNumber; diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 0a49040f7..398337a38 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -318,10 +318,10 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack * boxPosition = owner.fieldController->hexPositionLocal(frontPos).center() + Point(-8, -14); } - Point textPosition = Point(amountBG->dimensions().x/2 + boxPosition.x, boxPosition.y + graphics->fonts[EFonts::FONT_TINY]->getLineHeight() - 6); + Point textPosition = Point(amountBG->dimensions().x/2 + boxPosition.x, boxPosition.y + amountBG->dimensions().y/2); canvas.draw(amountBG, boxPosition); - canvas.drawText(textPosition, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::TOPCENTER, TextOperations::formatMetric(stack->getCount(), 4)); + canvas.drawText(textPosition, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, TextOperations::formatMetric(stack->getCount(), 4)); } void BattleStacksController::showStack(Canvas & canvas, const CStack * stack) @@ -862,7 +862,8 @@ std::vector BattleStacksController::selectHoveredStacks() spell = owner.actionsController->getCurrentSpell(hoveredHex); caster = owner.actionsController->getCurrentSpellcaster(); - if(caster && spell && owner.actionsController->currentActionSpellcasting(hoveredHex) ) //when casting spell + //casting spell or in explicit spellcasting mode that also handles SPELL_LIKE_ATTACK + if(caster && spell && (owner.actionsController->currentActionSpellcasting(hoveredHex) || owner.actionsController->creatureSpellcastingModeActive())) { spells::Target target; target.emplace_back(hoveredHex); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index d6e162486..0e6ba0f8e 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -538,7 +538,7 @@ void BattleWindow::tacticPhaseEnded() void BattleWindow::bOptionsf() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; CCS->curh->set(Cursor::Map::POINTER); @@ -548,7 +548,7 @@ void BattleWindow::bOptionsf() void BattleWindow::bSurrenderf() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; int cost = owner.getBattle()->battleGetSurrenderCost(); @@ -568,7 +568,7 @@ void BattleWindow::bSurrenderf() void BattleWindow::bFleef() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; if ( owner.getBattle()->battleCanFlee() ) @@ -675,7 +675,7 @@ void BattleWindow::setAlternativeActions(const std::listspellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; if(settings["battle"]["endWithAutocombat"].Bool() && onlyOnePlayerHuman) @@ -712,7 +712,7 @@ void BattleWindow::bAutofightf() void BattleWindow::bSpellf() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; if (!owner.makingTurn()) @@ -785,7 +785,7 @@ void BattleWindow::bSwitchActionf() void BattleWindow::bWaitf() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; if (owner.stacksController->getActiveStack() != nullptr) @@ -794,7 +794,7 @@ void BattleWindow::bWaitf() void BattleWindow::bDefencef() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; if (owner.stacksController->getActiveStack() != nullptr) @@ -803,7 +803,7 @@ void BattleWindow::bDefencef() void BattleWindow::bConsoleUpf() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; console->scrollUp(); @@ -811,7 +811,7 @@ void BattleWindow::bConsoleUpf() void BattleWindow::bConsoleDownf() { - if (owner.actionsController->spellcastingModeActive()) + if (owner.actionsController->heroSpellcastingModeActive()) return; console->scrollDown(); @@ -851,8 +851,8 @@ void BattleWindow::blockUI(bool on) setShortcutBlocked(EShortcut::BATTLE_WAIT, on || owner.tacticsMode || !canWait); setShortcutBlocked(EShortcut::BATTLE_DEFEND, on || owner.tacticsMode); setShortcutBlocked(EShortcut::BATTLE_SELECT_ACTION, on || owner.tacticsMode); - setShortcutBlocked(EShortcut::BATTLE_AUTOCOMBAT, (settings["battle"]["endWithAutocombat"].Bool() && onlyOnePlayerHuman) ? on || owner.tacticsMode || owner.actionsController->spellcastingModeActive() : owner.actionsController->spellcastingModeActive()); - setShortcutBlocked(EShortcut::BATTLE_END_WITH_AUTOCOMBAT, on || owner.tacticsMode || !onlyOnePlayerHuman || owner.actionsController->spellcastingModeActive()); + setShortcutBlocked(EShortcut::BATTLE_AUTOCOMBAT, (settings["battle"]["endWithAutocombat"].Bool() && onlyOnePlayerHuman) ? on || owner.tacticsMode || owner.actionsController->heroSpellcastingModeActive() : owner.actionsController->heroSpellcastingModeActive()); + setShortcutBlocked(EShortcut::BATTLE_END_WITH_AUTOCOMBAT, on || owner.tacticsMode || !onlyOnePlayerHuman || owner.actionsController->heroSpellcastingModeActive()); setShortcutBlocked(EShortcut::BATTLE_TACTICS_END, on || !owner.tacticsMode); setShortcutBlocked(EShortcut::BATTLE_TACTICS_NEXT, on || !owner.tacticsMode); setShortcutBlocked(EShortcut::BATTLE_CONSOLE_DOWN, on && !owner.tacticsMode); diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 55fe69e16..ebacdc1b4 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -192,7 +192,8 @@ void CGuiHandler::drawFPSCounter() std::string fps = std::to_string(framerate().getFramerate())+" FPS"; - graphics->fonts[FONT_SMALL]->renderTextLeft(screen, fps, Colors::WHITE, Point(8 * scaling, screen->h-22 * scaling)); + const auto & font = GH.renderHandler().loadFont(FONT_SMALL); + font->renderTextLeft(screen, fps, Colors::WHITE, Point(8 * scaling, screen->h-22 * scaling)); } bool CGuiHandler::amIGuiThread() diff --git a/client/gui/EventDispatcher.cpp b/client/gui/EventDispatcher.cpp index b43f8d209..94bbfcbe0 100644 --- a/client/gui/EventDispatcher.cpp +++ b/client/gui/EventDispatcher.cpp @@ -254,6 +254,10 @@ void EventDispatcher::handleLeftButtonClick(const Point & position, int toleranc i->mouseClickedState = isPressed; i->clickCancel(position); } + else if(isPressed) + { + i->notFocusedClick(); + } } } } diff --git a/client/gui/EventsReceiver.h b/client/gui/EventsReceiver.h index da194890f..5e09d800c 100644 --- a/client/gui/EventsReceiver.h +++ b/client/gui/EventsReceiver.h @@ -51,6 +51,7 @@ public: virtual void clickCancel(const Point & cursorPosition) {} virtual void showPopupWindow(const Point & cursorPosition) {} virtual void clickDouble(const Point & cursorPosition) {} + virtual void notFocusedClick() {}; /// Called when user pans screen by specified distance virtual void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) {} diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 2e2bd313c..8771898cc 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -19,6 +19,7 @@ #include "../gui/Shortcut.h" #include "../render/Graphics.h" #include "../render/IFont.h" +#include "../render/IRenderHandler.h" #include "../widgets/CComponent.h" #include "../widgets/ComboBox.h" #include "../widgets/Buttons.h" @@ -284,7 +285,7 @@ EFonts InterfaceObjectConfigurable::readFont(const JsonNode & config) const return EFonts::FONT_CALLI; } logGlobal->debug("Unknown font attribute"); - return EFonts::FONT_TIMES; + return EFonts::FONT_MEDIUM; } std::pair InterfaceObjectConfigurable::readHintText(const JsonNode & config) const @@ -357,8 +358,9 @@ std::shared_ptr InterfaceObjectConfigurable::buildMultiLineLabe auto color = readColor(config["color"]); auto text = readText(config["text"]); Rect rect = readRect(config["rect"]); + const auto & fontPtr = GH.renderHandler().loadFont(font); if(!config["adoptHeight"].isNull() && config["adoptHeight"].Bool()) - rect.h = graphics->fonts[font]->getLineHeight() * 2; + rect.h = fontPtr->getLineHeight() * 2; return std::make_shared(rect, font, alignment, color, text); } diff --git a/client/lobby/CBonusSelection.cpp b/client/lobby/CBonusSelection.cpp index b4dcc7f8e..d1db2d671 100644 --- a/client/lobby/CBonusSelection.cpp +++ b/client/lobby/CBonusSelection.cpp @@ -55,6 +55,7 @@ #include "../../lib/campaign/CampaignState.h" #include "../../lib/mapping/CMapService.h" #include "../../lib/mapping/CMapInfo.h" +#include "../../lib/mapping/CMapHeader.h" #include "../../lib/mapObjects/CGHeroInstance.h" @@ -126,9 +127,11 @@ CBonusSelection::CBonusSelection() for(auto scenarioID : getCampaign()->allScenarios()) { if(getCampaign()->isAvailable(scenarioID)) - regions.push_back(std::make_shared(scenarioID, true, true, getCampaign()->getRegions())); + regions.push_back(std::make_shared(scenarioID, true, true, false, getCampaign()->getRegions())); else if(getCampaign()->isConquered(scenarioID)) //display as striped - regions.push_back(std::make_shared(scenarioID, false, false, getCampaign()->getRegions())); + regions.push_back(std::make_shared(scenarioID, false, false, false, getCampaign()->getRegions())); + else + regions.push_back(std::make_shared(scenarioID, false, false, true, getCampaign()->getRegions())); } if (!getCampaign()->getMusic().empty()) @@ -400,6 +403,7 @@ void CBonusSelection::goBack() CSH->state = EClientState::LOBBY; } */ + CMM->playMusic(); } void CBonusSelection::startMap() @@ -476,8 +480,8 @@ void CBonusSelection::decreaseDifficulty() CSH->setDifficulty(CSH->si->difficulty - 1); } -CBonusSelection::CRegion::CRegion(CampaignScenarioID id, bool accessible, bool selectable, const CampaignRegions & campDsc) - : CIntObject(LCLICK | SHOW_POPUP), idOfMapAndRegion(id), accessible(accessible), selectable(selectable) +CBonusSelection::CRegion::CRegion(CampaignScenarioID id, bool accessible, bool selectable, bool labelOnly, const CampaignRegions & campDsc) + : CIntObject(LCLICK | SHOW_POPUP), idOfMapAndRegion(id), accessible(accessible), selectable(selectable), labelOnly(labelOnly) { OBJECT_CONSTRUCTION; @@ -493,10 +497,20 @@ CBonusSelection::CRegion::CRegion(CampaignScenarioID id, bool accessible, bool s graphicsStriped->disable(); pos.w = graphicsNotSelected->pos.w; pos.h = graphicsNotSelected->pos.h; + + auto labelPos = campDsc.getLabelPosition(id); + if(labelPos) + { + auto mapHeader = CSH->si->campState->getMapHeader(idOfMapAndRegion); + label = std::make_shared((*labelPos).x, (*labelPos).y, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, mapHeader->name.toString()); + } } void CBonusSelection::CRegion::updateState() { + if(labelOnly) + return; + if(!accessible) { graphicsNotSelected->disable(); @@ -519,7 +533,7 @@ void CBonusSelection::CRegion::updateState() void CBonusSelection::CRegion::clickReleased(const Point & cursorPosition) { - if(selectable && !graphicsNotSelected->getSurface()->isTransparent(cursorPosition - pos.topLeft())) + if(!labelOnly && selectable && !graphicsNotSelected->getSurface()->isTransparent(cursorPosition - pos.topLeft())) { CSH->setCampaignMap(idOfMapAndRegion); } @@ -529,7 +543,7 @@ void CBonusSelection::CRegion::showPopupWindow(const Point & cursorPosition) { // FIXME: For some reason "down" is only ever contain indeterminate_value auto & text = CSH->si->campState->scenario(idOfMapAndRegion).regionText; - if(!graphicsNotSelected->getSurface()->isTransparent(cursorPosition - pos.topLeft()) && !text.empty()) + if(!labelOnly && !graphicsNotSelected->getSurface()->isTransparent(cursorPosition - pos.topLeft()) && !text.empty()) { CRClickPopup::createAndPush(text.toString()); } diff --git a/client/lobby/CBonusSelection.h b/client/lobby/CBonusSelection.h index b9b341c60..7b26bcc88 100644 --- a/client/lobby/CBonusSelection.h +++ b/client/lobby/CBonusSelection.h @@ -49,8 +49,10 @@ public: CampaignScenarioID idOfMapAndRegion; bool accessible; // false if region should be striped bool selectable; // true if region should be selectable + bool labelOnly; + std::shared_ptr label; public: - CRegion(CampaignScenarioID id, bool accessible, bool selectable, const CampaignRegions & campDsc); + CRegion(CampaignScenarioID id, bool accessible, bool selectable, bool labelOnly, const CampaignRegions & campDsc); void updateState(); void clickReleased(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override; diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index 61ca27530..9a767f5f7 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -256,15 +256,17 @@ void InfoCard::changeSelection() if(!showChat) labelGroupPlayers->disable(); + const auto & font = GH.renderHandler().loadFont(FONT_SMALL); + for(const auto & p : CSH->playerNames) { int slotsUsed = labelGroupPlayers->currentSize(); Point labelPosition; if(slotsUsed < 4) - labelPosition = Point(24, 285 + slotsUsed * graphics->fonts[FONT_SMALL]->getLineHeight()); // left column + labelPosition = Point(24, 285 + slotsUsed * font->getLineHeight()); // left column else - labelPosition = Point(193, 285 + (slotsUsed - 4) * graphics->fonts[FONT_SMALL]->getLineHeight()); // right column + labelPosition = Point(193, 285 + (slotsUsed - 4) * font->getLineHeight()); // right column labelGroupPlayers->add(labelPosition.x, labelPosition.y, p.second.name); } @@ -358,7 +360,8 @@ CChatBox::CChatBox(const Rect & rect) pos += rect.topLeft(); setRedrawParent(true); - const int height = static_cast(graphics->fonts[FONT_SMALL]->getLineHeight()); + const auto & font = GH.renderHandler().loadFont(FONT_SMALL); + const int height = font->getLineHeight(); Rect textInputArea(1, rect.h - height, rect.w - 1, height); Rect chatHistoryArea(3, 1, rect.w - 3, rect.h - height - 1); inputBackground = std::make_shared(textInputArea, ColorRGBA(0,0,0,192)); diff --git a/client/lobby/OptionsTab.cpp b/client/lobby/OptionsTab.cpp index c63b8ca38..674bb81ee 100644 --- a/client/lobby/OptionsTab.cpp +++ b/client/lobby/OptionsTab.cpp @@ -19,6 +19,7 @@ #include "../gui/WindowHandler.h" #include "../render/Graphics.h" #include "../render/IFont.h" +#include "../render/IRenderHandler.h" #include "../media/ISoundPlayer.h" #include "../widgets/CComponent.h" #include "../widgets/ComboBox.h" @@ -1034,12 +1035,14 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con labelPlayerNameEdit = std::make_shared(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, false); labelPlayerNameEdit->setText(name); } - labelWhoCanPlay = std::make_shared(Rect(6, 23, 45, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]); + const auto & font = GH.renderHandler().loadFont(FONT_SMALL); + + labelWhoCanPlay = std::make_shared(Rect(6, 23, 45, font->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]); auto hasHandicap = [this](){ return s->handicap.startBonus.empty() && s->handicap.percentIncome == 100 && s->handicap.percentGrowth == 100; }; std::string labelHandicapText = hasHandicap() ? CGI->generaltexth->arraytxt[210] : MetaString::createFromTextID("vcmi.lobby.handicap").toString(); - labelHandicap = std::make_shared(Rect(57, 24, 47, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, labelHandicapText); - handicap = std::make_shared(Rect(56, 24, 49, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), [](){ + labelHandicap = std::make_shared(Rect(57, 24, 47, font->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, labelHandicapText); + handicap = std::make_shared(Rect(56, 24, 49, font->getLineHeight()*2), [](){ if(!CSH->isHost()) return; diff --git a/client/mapView/MapOverlayLogVisualizer.cpp b/client/mapView/MapOverlayLogVisualizer.cpp index a73a72a8a..7e1678230 100644 --- a/client/mapView/MapOverlayLogVisualizer.cpp +++ b/client/mapView/MapOverlayLogVisualizer.cpp @@ -17,8 +17,10 @@ #include "../render/Colors.h" #include "../render/EFont.h" #include "../render/IFont.h" +#include "../render/IRenderHandler.h" #include "../render/Graphics.h" #include "../gui/TextAlignment.h" +#include "../gui/CGuiHandler.h" MapOverlayLogVisualizer::MapOverlayLogVisualizer(Canvas & target, std::shared_ptr model) @@ -78,8 +80,10 @@ void MapOverlayLogVisualizer::drawText( if(viewPort.isInside(pStart)) { - int w = graphics->fonts[EFonts::FONT_TINY]->getStringWidth(text); - int h = graphics->fonts[EFonts::FONT_TINY]->getLineHeight(); + const auto & font = GH.renderHandler().loadFont(FONT_TINY); + + int w = font->getStringWidth(text); + int h = font->getLineHeight(); pStart.y += h * lineNumber; diff --git a/client/mapView/MapViewCache.cpp b/client/mapView/MapViewCache.cpp index bac30fcf2..34c06d0b7 100644 --- a/client/mapView/MapViewCache.cpp +++ b/client/mapView/MapViewCache.cpp @@ -188,6 +188,8 @@ void MapViewCache::render(const std::shared_ptr & context, if(context->showTextOverlay()) { + const auto & font = GH.renderHandler().loadFont(FONT_TINY); + for(int y = dimensions.top(); y < dimensions.bottom(); ++y) { for(int x = dimensions.left(); x < dimensions.right(); ++x) @@ -204,8 +206,6 @@ void MapViewCache::render(const std::shared_ptr & context, else position.y -= targetRect.h / 4; - const auto font = graphics->fonts[EFonts::FONT_TINY]; - Point dimensions(font->getStringWidth(overlay), font->getLineHeight()); Rect textRect = Rect(position - dimensions / 2, dimensions).resize(2); diff --git a/client/render/Canvas.cpp b/client/render/Canvas.cpp index 682349bbe..9cb8d551f 100644 --- a/client/render/Canvas.cpp +++ b/client/render/Canvas.cpp @@ -11,6 +11,7 @@ #include "Canvas.h" #include "../gui/CGuiHandler.h" +#include "../render/IRenderHandler.h" #include "../render/IScreenHandler.h" #include "../renderSDL/SDL_Extensions.h" #include "Colors.h" @@ -169,23 +170,27 @@ void Canvas::drawBorderDashed(const Rect & target, const ColorRGBA & color) void Canvas::drawText(const Point & position, const EFonts & font, const ColorRGBA & colorDest, ETextAlignment alignment, const std::string & text ) { + const auto & fontPtr = GH.renderHandler().loadFont(font); + switch (alignment) { - case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLeft (surface, text, colorDest, transformPos(position)); - case ETextAlignment::TOPCENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, transformPos(position)); - case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, transformPos(position)); - case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextRight (surface, text, colorDest, transformPos(position)); + case ETextAlignment::TOPLEFT: return fontPtr->renderTextLeft (surface, text, colorDest, transformPos(position)); + case ETextAlignment::TOPCENTER: return fontPtr->renderTextCenter(surface, text, colorDest, transformPos(position)); + case ETextAlignment::CENTER: return fontPtr->renderTextCenter(surface, text, colorDest, transformPos(position)); + case ETextAlignment::BOTTOMRIGHT: return fontPtr->renderTextRight (surface, text, colorDest, transformPos(position)); } } void Canvas::drawText(const Point & position, const EFonts & font, const ColorRGBA & colorDest, ETextAlignment alignment, const std::vector & text ) { + const auto & fontPtr = GH.renderHandler().loadFont(font); + switch (alignment) { - case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLinesLeft (surface, text, colorDest, transformPos(position)); - case ETextAlignment::TOPCENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, transformPos(position)); - case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, transformPos(position)); - case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, transformPos(position)); + case ETextAlignment::TOPLEFT: return fontPtr->renderTextLinesLeft (surface, text, colorDest, transformPos(position)); + case ETextAlignment::TOPCENTER: return fontPtr->renderTextLinesCenter(surface, text, colorDest, transformPos(position)); + case ETextAlignment::CENTER: return fontPtr->renderTextLinesCenter(surface, text, colorDest, transformPos(position)); + case ETextAlignment::BOTTOMRIGHT: return fontPtr->renderTextLinesRight (surface, text, colorDest, transformPos(position)); } } diff --git a/client/render/Canvas.h b/client/render/Canvas.h index ae9b14fb9..a421ab232 100644 --- a/client/render/Canvas.h +++ b/client/render/Canvas.h @@ -15,7 +15,7 @@ struct SDL_Surface; class IImage; -enum EFonts : int; +enum EFonts : int8_t; enum class CanvasScalingPolicy { diff --git a/client/render/EFont.h b/client/render/EFont.h index 7f5dfee41..316f3028f 100644 --- a/client/render/EFont.h +++ b/client/render/EFont.h @@ -9,7 +9,15 @@ */ #pragma once -enum EFonts : int +enum EFonts : int8_t { - FONT_BIG, FONT_CALLI, FONT_CREDITS, FONT_HIGH_SCORE, FONT_MEDIUM, FONT_SMALL, FONT_TIMES, FONT_TINY, FONT_VERD + FONT_BIG, + FONT_CALLI, + FONT_CREDITS, + FONT_HIGH_SCORE, + FONT_MEDIUM, + FONT_SMALL, + FONT_TIMES, + FONT_TINY, + FONT_VERD }; diff --git a/client/render/Graphics.cpp b/client/render/Graphics.cpp index 64c6e2b7f..0c88f508a 100644 --- a/client/render/Graphics.cpp +++ b/client/render/Graphics.cpp @@ -19,25 +19,15 @@ #include #include "../renderSDL/SDL_Extensions.h" -#include "../renderSDL/CBitmapFont.h" -#include "../renderSDL/CBitmapHanFont.h" -#include "../renderSDL/CTrueTypeFont.h" #include "../render/CAnimation.h" #include "../render/IImage.h" -#include "../render/IRenderHandler.h" -#include "../gui/CGuiHandler.h" #include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/CBinaryReader.h" #include "../../lib/json/JsonNode.h" #include "../lib/modding/CModHandler.h" #include "../lib/modding/ModScope.h" -#include "CGameInfo.h" #include "../lib/VCMI_Lib.h" -#include "../CCallback.h" -#include "../lib/texts/CGeneralTextHandler.h" -#include "../lib/vcmi_endian.h" -#include "../lib/CStopWatch.h" #include "../lib/CHeroHandler.h" #include @@ -127,7 +117,6 @@ void Graphics::initializeBattleGraphics() } Graphics::Graphics() { - loadFonts(); loadPaletteAndColors(); initializeBattleGraphics(); loadErmuToPicture(); @@ -166,29 +155,6 @@ void Graphics::setPlayerFlagColor(SDL_Palette * targetPalette, PlayerColor playe } } -void Graphics::loadFonts() -{ - const JsonNode config(JsonPath::builtin("config/fonts.json")); - - const JsonVector & bmpConf = config["bitmap"].Vector(); - const JsonNode & ttfConf = config["trueType"]; - const JsonNode & hanConf = config["bitmapHan"]; - - assert(bmpConf.size() == FONTS_NUMBER); - - for (size_t i=0; i(hanConf[filename]); - else if (!ttfConf[filename].isNull()) // no ttf override - fonts[i] = std::make_shared(ttfConf[filename]); - else - fonts[i] = std::make_shared(filename); - } -} - void Graphics::loadErmuToPicture() { //loading ERMU to picture diff --git a/client/render/Graphics.h b/client/render/Graphics.h index 8b4941b40..017f8e1b4 100644 --- a/client/render/Graphics.h +++ b/client/render/Graphics.h @@ -9,26 +9,11 @@ */ #pragma once -#include "../lib/GameConstants.h" +#include "../lib/constants/NumericConstants.h" +#include "../lib/constants/EntityIdentifiers.h" #include "../lib/Color.h" -#include "../lib/filesystem/ResourcePath.h" - -VCMI_LIB_NAMESPACE_BEGIN - -class CGHeroInstance; -class CGTownInstance; -class CHeroClass; -struct InfoAboutHero; -struct InfoAboutTown; -class CGObjectInstance; -class ObjectTemplate; -class EntityService; -class JsonNode; - -VCMI_LIB_NAMESPACE_END struct SDL_Palette; -class IFont; /// Handles fonts, hero images, town images, various graphics class Graphics @@ -36,13 +21,8 @@ class Graphics void initializeBattleGraphics(); void loadPaletteAndColors(); void loadErmuToPicture(); - void loadFonts(); public: - //Fonts - static const int FONTS_NUMBER = 9; - std::array< std::shared_ptr, FONTS_NUMBER> fonts; - using PlayerPalette = std::array; //various graphics diff --git a/client/render/IFont.cpp b/client/render/IFont.cpp index 3567a4da9..5e773de04 100644 --- a/client/render/IFont.cpp +++ b/client/render/IFont.cpp @@ -23,13 +23,33 @@ int IFont::getScalingFactor() const return GH.screenHandler().getScalingFactor(); } +size_t IFont::getLineHeight() const +{ + return getLineHeightScaled() / getScalingFactor(); +} + +size_t IFont::getGlyphWidth(const char * data) const +{ + return getGlyphWidthScaled(data) / getScalingFactor(); +} + size_t IFont::getStringWidth(const std::string & data) const +{ + return getStringWidthScaled(data) / getScalingFactor(); +} + +size_t IFont::getFontAscent() const +{ + return getFontAscentScaled() / getScalingFactor(); +} + +size_t IFont::getStringWidthScaled(const std::string & data) const { size_t width = 0; for(size_t i=0; i loadAnimation(const AnimationPath & path, EImageBlitMode mode) = 0; + + /// Returns font with specified identifer + virtual std::shared_ptr loadFont(EFonts font) = 0; }; diff --git a/client/renderSDL/CBitmapFont.cpp b/client/renderSDL/CBitmapFont.cpp index 7f30d75db..53a09460b 100644 --- a/client/renderSDL/CBitmapFont.cpp +++ b/client/renderSDL/CBitmapFont.cpp @@ -96,21 +96,14 @@ static AtlasLayout doAtlasPacking(const std::map & images) } } -void CBitmapFont::loadModFont(const std::string & modName, const ResourcePath & resource, std::unordered_map & loadedChars) +void CBitmapFont::loadFont(const ResourcePath & resource, std::unordered_map & loadedChars) { - if (!CResourceHandler::get(modName)->existsResource(resource)) - { - logGlobal->error("Failed to load font %s from mod %s", resource.getName(), modName); - return; - } - - auto data = CResourceHandler::get(modName)->load(resource)->readAll(); - std::string modLanguage = CGI->modh->getModLanguage(modName); + auto data = CResourceHandler::get()->load(resource)->readAll(); + std::string modName = VLC->modh->findResourceOrigin(resource); + std::string modLanguage = VLC->modh->getModLanguage(modName); std::string modEncoding = Languages::getLanguageOptions(modLanguage).encoding; - uint32_t dataHeight = data.first[5]; - - maxHeight = std::max(maxHeight, dataHeight); + height = data.first[5]; constexpr size_t symbolsInFile = 0x100; constexpr size_t baseIndex = 32; @@ -126,10 +119,10 @@ void CBitmapFont::loadModFont(const std::string & modName, const ResourcePath & symbol.leftOffset = read_le_u32(data.first.get() + baseIndex + charIndex * 12 + 0); symbol.width = read_le_u32(data.first.get() + baseIndex + charIndex * 12 + 4); symbol.rightOffset = read_le_u32(data.first.get() + baseIndex + charIndex * 12 + 8); - symbol.height = dataHeight; + symbol.height = height; uint32_t pixelDataOffset = read_le_u32(data.first.get() + offsetIndex + charIndex * 4); - uint32_t pixelsCount = dataHeight * symbol.width; + uint32_t pixelsCount = height * symbol.width; symbol.pixels.resize(pixelsCount); @@ -139,21 +132,27 @@ void CBitmapFont::loadModFont(const std::string & modName, const ResourcePath & loadedChars[codepoint] = symbol; } + + // Try to use symbol 'L' to detect font 'ascent' - number of pixels above text baseline + const auto & symbolL = loadedChars['L']; + uint32_t lastNonEmptyRow = 0; + for (uint32_t row = 0; row < symbolL.height; ++row) + { + for (uint32_t col = 0; col < symbolL.width; ++col) + if (symbolL.pixels.at(row * symbolL.width + col) == 255) + lastNonEmptyRow = std::max(lastNonEmptyRow, row); + } + + ascent = lastNonEmptyRow + 1; } CBitmapFont::CBitmapFont(const std::string & filename): - maxHeight(0) + height(0) { ResourcePath resource("data/" + filename, EResType::BMP_FONT); std::unordered_map loadedChars; - loadModFont("core", resource, loadedChars); - - for(const auto & modName : VLC->modh->getActiveMods()) - { - if (CResourceHandler::get(modName)->existsResource(resource)) - loadModFont(modName, resource, loadedChars); - } + loadFont(resource, loadedChars); std::map atlasSymbol; for (auto const & symbol : loadedChars) @@ -213,8 +212,6 @@ CBitmapFont::CBitmapFont(const std::string & filename): SDL_FreeSurface(atlasImage); atlasImage = scaledSurface; } - - IMG_SavePNG(atlasImage, ("/home/ivan/font_" + filename).c_str()); } CBitmapFont::~CBitmapFont() @@ -222,12 +219,12 @@ CBitmapFont::~CBitmapFont() SDL_FreeSurface(atlasImage); } -size_t CBitmapFont::getLineHeight() const +size_t CBitmapFont::getLineHeightScaled() const { - return maxHeight; + return height * getScalingFactor(); } -size_t CBitmapFont::getGlyphWidth(const char * data) const +size_t CBitmapFont::getGlyphWidthScaled(const char * data) const { CodePoint localChar = TextOperations::getUnicodeCodepoint(data, 4); @@ -236,7 +233,12 @@ size_t CBitmapFont::getGlyphWidth(const char * data) const if (iter == chars.end()) return 0; - return iter->second.leftOffset + iter->second.positionInAtlas.w + iter->second.rightOffset; + return (iter->second.leftOffset + iter->second.positionInAtlas.w + iter->second.rightOffset) * getScalingFactor(); +} + +size_t CBitmapFont::getFontAscentScaled() const +{ + return ascent * getScalingFactor(); } bool CBitmapFont::canRepresentCharacter(const char *data) const diff --git a/client/renderSDL/CBitmapFont.h b/client/renderSDL/CBitmapFont.h index fc5a8253a..78bb6aad2 100644 --- a/client/renderSDL/CBitmapFont.h +++ b/client/renderSDL/CBitmapFont.h @@ -40,9 +40,10 @@ class CBitmapFont final : public IFont }; std::unordered_map chars; - uint32_t maxHeight; + uint32_t height; + uint32_t ascent; - void loadModFont(const std::string & modName, const ResourcePath & resource, std::unordered_map & loadedChars); + void loadFont(const ResourcePath & resource, std::unordered_map & loadedChars); void renderCharacter(SDL_Surface * surface, const BitmapChar & character, const ColorRGBA & color, int &posX, int &posY) const; void renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const override; @@ -50,11 +51,12 @@ public: explicit CBitmapFont(const std::string & filename); ~CBitmapFont(); - size_t getLineHeight() const override; - size_t getGlyphWidth(const char * data) const override; + size_t getFontAscentScaled() const override; + size_t getLineHeightScaled() const override; + size_t getGlyphWidthScaled(const char * data) const override; /// returns true if this font contains provided utf-8 character - bool canRepresentCharacter(const char * data) const; + bool canRepresentCharacter(const char * data) const override; bool canRepresentString(const std::string & data) const; friend class CBitmapHanFont; diff --git a/client/renderSDL/CBitmapHanFont.cpp b/client/renderSDL/CBitmapHanFont.cpp deleted file mode 100644 index d85d0ebcd..000000000 --- a/client/renderSDL/CBitmapHanFont.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * CBitmapHanFont.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 "CBitmapHanFont.h" - -#include "CBitmapFont.h" -#include "SDL_Extensions.h" - -#include "../../lib/filesystem/Filesystem.h" -#include "../../lib/json/JsonNode.h" -#include "../../lib/Rect.h" -#include "../../lib/texts/TextOperations.h" - -#include - -size_t CBitmapHanFont::getCharacterDataOffset(size_t index) const -{ - size_t rowSize = (size + 7) / 8; // 1 bit per pixel, rounded up - size_t charSize = rowSize * size; // glyph contains "size" rows - return index * charSize; -} - -size_t CBitmapHanFont::getCharacterIndex(ui8 first, ui8 second) const -{ - if (second > 0x7f ) - second--; - - return (first - 0x81) * (12*16 - 2) + (second - 0x40); -} - -void CBitmapHanFont::renderCharacter(SDL_Surface * surface, int characterIndex, const ColorRGBA & color, int &posX, int &posY) const -{ - //TODO: somewhat duplicated with CBitmapFont::renderCharacter(); - Rect clipRect; - CSDL_Ext::getClipRect(surface, clipRect); - - CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface); - uint8_t bpp = surface->format->BytesPerPixel; - - // start of line, may differ from 0 due to end of surface or clipped surface - int lineBegin = std::max(0, clipRect.y - posY); - int lineEnd = std::min((int)size, clipRect.y + clipRect.h - posY); - - // start end end of each row, may differ from 0 - int rowBegin = std::max(0, clipRect.x - posX); - int rowEnd = std::min((int)size, clipRect.x + clipRect.w - posX); - - //for each line in symbol - for(int dy = lineBegin; dy pixels; - uint8_t *source = data.first.get() + getCharacterDataOffset(characterIndex); - - // shift source\destination pixels to current position - dstLine += (posY+dy) * surface->pitch + posX * bpp; - source += ((size + 7) / 8) * dy; - - //for each column in line - for(int dx = rowBegin; dx < rowEnd; dx++) - { - // select current bit in bitmap - int bit = (source[dx / 8] << (dx % 8)) & 0x80; - - uint8_t* dstPixel = dstLine + dx*bpp; - if (bit != 0) - colorPutter(dstPixel, color.r, color.g, color.b); - } - } - posX += (int)size + 1; -} - -void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const -{ - int posX = pos.x; - int posY = pos.y; - - SDL_LockSurface(surface); - - for(size_t i=0; irenderCharacter(surface, fallback->chars[ui8(localChar[0])], color, posX, posY); - - if (localChar.size() == 2) - renderCharacter(surface, (int)getCharacterIndex(localChar[0], localChar[1]), color, posX, posY); - } - SDL_UnlockSurface(surface); -} - -CBitmapHanFont::CBitmapHanFont(const JsonNode &config): - fallback(new CBitmapFont(config["fallback"].String())), - data(CResourceHandler::get()->load(ResourcePath("data/" + config["name"].String(), EResType::OTHER))->readAll()), - size((size_t)config["size"].Float()) -{ - // basic tests to make sure that fonts are OK - // 1) fonts must contain 190 "sections", 126 symbols each. - assert(getCharacterIndex(0xfe, 0xff) == 190*126); - // 2) ensure that font size is correct - enough to fit all possible symbols - assert(getCharacterDataOffset(getCharacterIndex(0xfe, 0xff)) == data.second); -} - -size_t CBitmapHanFont::getLineHeight() const -{ - return std::max(size + 1, fallback->getLineHeight()); -} - -size_t CBitmapHanFont::getGlyphWidth(const char * data) const -{ - std::string localChar = TextOperations::fromUnicode(std::string(data, TextOperations::getUnicodeCharacterSize(data[0])), "GBK"); - - if (localChar.size() == 1) - return fallback->getGlyphWidth(data); - - if (localChar.size() == 2) - return size + 1; - - return 0; -} diff --git a/client/renderSDL/CBitmapHanFont.h b/client/renderSDL/CBitmapHanFont.h deleted file mode 100644 index 5181787b7..000000000 --- a/client/renderSDL/CBitmapHanFont.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * CBitmapHanFont.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 - -#include "../render/IFont.h" - -VCMI_LIB_NAMESPACE_BEGIN -class JsonNode; -VCMI_LIB_NAMESPACE_END - -class CBitmapFont; - -/// supports multi-byte characters for such languages like Chinese -class CBitmapHanFont final : public IFont -{ - std::unique_ptr fallback; - // data, directly copied from file - const std::pair, ui64> data; - - // size of the font. Not available in file but needed for proper rendering - const size_t size; - - size_t getCharacterDataOffset(size_t index) const; - size_t getCharacterIndex(ui8 first, ui8 second) const; - - void renderCharacter(SDL_Surface * surface, int characterIndex, const ColorRGBA & color, int &posX, int &posY) const; - void renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const override; -public: - CBitmapHanFont(const JsonNode & config); - - size_t getLineHeight() const override; - size_t getGlyphWidth(const char * data) const override; -}; diff --git a/client/renderSDL/CTrueTypeFont.cpp b/client/renderSDL/CTrueTypeFont.cpp index deda94b70..2119ba14a 100644 --- a/client/renderSDL/CTrueTypeFont.cpp +++ b/client/renderSDL/CTrueTypeFont.cpp @@ -15,6 +15,7 @@ #include "../render/Colors.h" #include "../renderSDL/SDL_Extensions.h" +#include "../../lib/CConfigHandler.h" #include "../../lib/json/JsonNode.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/texts/TextOperations.h" @@ -29,12 +30,13 @@ std::pair, ui64> CTrueTypeFont::loadData(const JsonNode & int CTrueTypeFont::getPointSize(const JsonNode & config) const { + float fontScale = settings["video"]["fontScalingFactor"].Float(); int scalingFactor = getScalingFactor(); if (config.isNumber()) - return config.Integer() * scalingFactor; + return std::round(config.Integer() * scalingFactor * fontScale); else - return config[scalingFactor-1].Integer(); + return std::round(config[scalingFactor-1].Integer() * fontScale); } TTF_Font * CTrueTypeFont::loadFont(const JsonNode &config) @@ -62,57 +64,65 @@ int CTrueTypeFont::getFontStyle(const JsonNode &config) const CTrueTypeFont::CTrueTypeFont(const JsonNode & fontConfig): data(loadData(fontConfig)), font(loadFont(fontConfig), TTF_CloseFont), - dropShadow(fontConfig["blend"].Bool()), - blended(fontConfig["blend"].Bool()) + dropShadow(!fontConfig["noShadow"].Bool()), + outline(fontConfig["outline"].Bool()), + blended(true) { assert(font); TTF_SetFontStyle(font.get(), getFontStyle(fontConfig)); + TTF_SetFontHinting(font.get(),TTF_HINTING_MONO); - std::string fallbackName = fontConfig["fallback"].String(); - - if (!fallbackName.empty()) - fallbackFont = std::make_unique(fallbackName); } CTrueTypeFont::~CTrueTypeFont() = default; -size_t CTrueTypeFont::getLineHeight() const +size_t CTrueTypeFont::getFontAscentScaled() const { - if (fallbackFont) - return fallbackFont->getLineHeight(); - - return TTF_FontHeight(font.get()) / getScalingFactor(); + return TTF_FontAscent(font.get()); } -size_t CTrueTypeFont::getGlyphWidth(const char *data) const +size_t CTrueTypeFont::getLineHeightScaled() const { - if (fallbackFont && fallbackFont->canRepresentCharacter(data)) - return fallbackFont->getGlyphWidth(data); - - return getStringWidth(std::string(data, TextOperations::getUnicodeCharacterSize(*data))); + return TTF_FontHeight(font.get()); } -size_t CTrueTypeFont::getStringWidth(const std::string & data) const +size_t CTrueTypeFont::getGlyphWidthScaled(const char *data) const { - if (fallbackFont && fallbackFont->canRepresentString(data)) - return fallbackFont->getStringWidth(data); + return getStringWidthScaled(std::string(data, TextOperations::getUnicodeCharacterSize(*data))); +} +bool CTrueTypeFont::canRepresentCharacter(const char * data) const +{ + uint32_t codepoint = TextOperations::getUnicodeCodepoint(data, TextOperations::getUnicodeCharacterSize(*data)); +#if SDL_TTF_VERSION_ATLEAST(2, 0, 18) + return TTF_GlyphIsProvided32(font.get(), codepoint); +#elif SDL_TTF_VERSION_ATLEAST(2, 0, 12) + if (codepoint <= 0xffff) + return TTF_GlyphIsProvided(font.get(), codepoint); + return true; +#else + return true; +#endif +} + +size_t CTrueTypeFont::getStringWidthScaled(const std::string & data) const +{ int width; TTF_SizeUTF8(font.get(), data.c_str(), &width, nullptr); - return width / getScalingFactor(); + return width; } void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const { - if (fallbackFont && fallbackFont->canRepresentString(data)) + if (color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow { - fallbackFont->renderText(surface, data, color, pos); - return; - } + if (outline) + renderText(surface, data, Colors::BLACK, pos - Point(1,1) * getScalingFactor()); - if (dropShadow && color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow - renderText(surface, data, Colors::BLACK, pos + Point(1,1) * getScalingFactor()); + if (dropShadow || outline) + renderText(surface, data, Colors::BLACK, pos + Point(1,1) * getScalingFactor()); + } if (!data.empty()) { diff --git a/client/renderSDL/CTrueTypeFont.h b/client/renderSDL/CTrueTypeFont.h index fe6cb6bd0..87a9ac484 100644 --- a/client/renderSDL/CTrueTypeFont.h +++ b/client/renderSDL/CTrueTypeFont.h @@ -21,11 +21,11 @@ using TTF_Font = struct _TTF_Font; class CTrueTypeFont final : public IFont { - std::unique_ptr fallbackFont; const std::pair, ui64> data; const std::unique_ptr font; const bool blended; + const bool outline; const bool dropShadow; std::pair, ui64> loadData(const JsonNode & config); @@ -34,11 +34,14 @@ class CTrueTypeFont final : public IFont int getFontStyle(const JsonNode & config) const; void renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const override; + size_t getFontAscentScaled() const override; public: CTrueTypeFont(const JsonNode & fontConfig); ~CTrueTypeFont(); - size_t getLineHeight() const override; - size_t getGlyphWidth(const char * data) const override; - size_t getStringWidth(const std::string & data) const override; + bool canRepresentCharacter(const char * data) const override; + + size_t getLineHeightScaled() const override; + size_t getGlyphWidthScaled(const char * data) const override; + size_t getStringWidthScaled(const std::string & data) const override; }; diff --git a/client/renderSDL/FontChain.cpp b/client/renderSDL/FontChain.cpp new file mode 100644 index 000000000..44f71097f --- /dev/null +++ b/client/renderSDL/FontChain.cpp @@ -0,0 +1,136 @@ +/* + * FontChain.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 "FontChain.h" + +#include "CTrueTypeFont.h" +#include "CBitmapFont.h" + +#include "../../lib/CConfigHandler.h" +#include "../../lib/texts/TextOperations.h" + +void FontChain::renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const +{ + auto chunks = splitTextToChunks(data); + int maxAscent = getFontAscentScaled(); + Point currentPos = pos; + for (auto const & chunk : chunks) + { + Point chunkPos = currentPos; + int currAscent = chunk.font->getFontAscentScaled(); + chunkPos.y += maxAscent - currAscent; + chunk.font->renderText(surface, chunk.text, color, chunkPos); + currentPos.x += chunk.font->getStringWidthScaled(chunk.text); + } +} + +size_t FontChain::getFontAscentScaled() const +{ + size_t maxHeight = 0; + for(const auto & font : chain) + maxHeight = std::max(maxHeight, font->getFontAscentScaled()); + return maxHeight; +} + +bool FontChain::bitmapFontsPrioritized() const +{ + const std::string & fontType = settings["video"]["fontsType"].String(); + if (fontType == "original") + return true; + if (fontType == "scalable") + return false; + + // else - autoselection. + + if (getScalingFactor() != 1) + return false; // If xbrz in use ttf/scalable fonts are preferred + + if (!vstd::isAlmostEqual(1.0, settings["video"]["fontScalingFactor"].Float())) + return false; // If player requested non-100% scaling - use scalable fonts + + return true; // else - use original bitmap fonts +} + +void FontChain::addTrueTypeFont(const JsonNode & trueTypeConfig) +{ + chain.insert(chain.begin(), std::make_unique(trueTypeConfig)); +} + +void FontChain::addBitmapFont(const std::string & bitmapFilename) +{ + if (bitmapFontsPrioritized()) + chain.insert(chain.begin(), std::make_unique(bitmapFilename)); + else + chain.push_back(std::make_unique(bitmapFilename)); +} + +bool FontChain::canRepresentCharacter(const char * data) const +{ + for(const auto & font : chain) + if (font->canRepresentCharacter(data)) + return true; + return false; +} + +size_t FontChain::getLineHeightScaled() const +{ + size_t maxHeight = 0; + for(const auto & font : chain) + maxHeight = std::max(maxHeight, font->getLineHeightScaled()); + return maxHeight; +} + +size_t FontChain::getGlyphWidthScaled(const char * data) const +{ + for(const auto & font : chain) + if (font->canRepresentCharacter(data)) + return font->getGlyphWidthScaled(data); + return 0; +} + +std::vector FontChain::splitTextToChunks(const std::string & data) const +{ + std::vector chunks; + + for (size_t i = 0; i < data.size(); i += TextOperations::getUnicodeCharacterSize(data[i])) + { + const IFont * currentFont = nullptr; + for(const auto & font : chain) + { + if (font->canRepresentCharacter(data.data() + i)) + { + currentFont = font.get(); + break; + } + } + + if (currentFont == nullptr) + continue; // not representable + + std::string symbol = data.substr(i, TextOperations::getUnicodeCharacterSize(data[i])); + + if (chunks.empty() || chunks.back().font != currentFont) + chunks.push_back({currentFont, symbol}); + else + chunks.back().text += symbol; + } + + return chunks; +} + +size_t FontChain::getStringWidthScaled(const std::string & data) const +{ + size_t result = 0; + auto chunks = splitTextToChunks(data); + for (auto const & chunk : chunks) + result += chunk.font->getStringWidthScaled(chunk.text); + + return result; +} diff --git a/client/renderSDL/FontChain.h b/client/renderSDL/FontChain.h new file mode 100644 index 000000000..b07902cd4 --- /dev/null +++ b/client/renderSDL/FontChain.h @@ -0,0 +1,43 @@ +/* + * FontChain.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 + +#include "../render/IFont.h" + +VCMI_LIB_NAMESPACE_BEGIN +class JsonNode; +VCMI_LIB_NAMESPACE_END + +class FontChain final : public IFont +{ + struct TextChunk + { + const IFont * font; + std::string text; + }; + + std::vector splitTextToChunks(const std::string & data) const; + + std::vector> chain; + + void renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const override; + size_t getFontAscentScaled() const override; + bool bitmapFontsPrioritized() const; +public: + FontChain() = default; + + void addTrueTypeFont(const JsonNode & trueTypeConfig); + void addBitmapFont(const std::string & bitmapFilename); + + size_t getLineHeightScaled() const override; + size_t getGlyphWidthScaled(const char * data) const override; + size_t getStringWidthScaled(const std::string & data) const override; + bool canRepresentCharacter(const char * data) const override; +}; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 88207644e..be0d5db3b 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -12,6 +12,7 @@ #include "SDLImage.h" #include "ImageScaled.h" +#include "FontChain.h" #include "../gui/CGuiHandler.h" @@ -20,7 +21,6 @@ #include "../render/Colors.h" #include "../render/ColorFilter.h" #include "../render/IScreenHandler.h" - #include "../../lib/json/JsonUtils.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/VCMIDirs.h" @@ -335,3 +335,32 @@ void RenderHandler::onLibraryLoadingFinished(const Services * services) addImageListEntries(services->spells()); addImageListEntries(services->skills()); } + +std::shared_ptr RenderHandler::loadFont(EFonts font) +{ + if (fonts.count(font)) + return fonts.at(font); + + const int8_t index = static_cast(font); + auto configList = CResourceHandler::get()->getResourcesWithName(JsonPath::builtin("config/fonts.json")); + std::shared_ptr loadedFont = std::make_shared(); + std::string bitmapPath; + + for(auto & loader : configList) + { + auto stream = loader->load(JsonPath::builtin("config/fonts.json")); + std::unique_ptr textData(new ui8[stream->getSize()]); + stream->read(textData.get(), stream->getSize()); + const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), "config/fonts.json"); + const JsonVector & bmpConf = config["bitmap"].Vector(); + const JsonNode & ttfConf = config["trueType"]; + + bitmapPath = bmpConf[index].String(); + if (!ttfConf[bitmapPath].isNull()) + loadedFont->addTrueTypeFont(ttfConf[bitmapPath]); + } + loadedFont->addBitmapFont(bitmapPath); + + fonts[font] = loadedFont; + return loadedFont; +} diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index ee874c0dd..20b13306a 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -26,6 +26,7 @@ class RenderHandler : public IRenderHandler std::map> animationFiles; std::map animationLayouts; std::map> imageFiles; + std::map> fonts; std::shared_ptr getAnimationFile(const AnimationPath & path); AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); @@ -59,4 +60,7 @@ public: std::shared_ptr loadAnimation(const AnimationPath & path, EImageBlitMode mode) override; std::shared_ptr createImage(SDL_Surface * source) override; + + /// Returns font with specified identifer + std::shared_ptr loadFont(EFonts font) override; }; diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 87d78d487..2ece8d515 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -21,6 +21,7 @@ #include "../gui/Shortcut.h" #include "../render/Canvas.h" #include "../render/IFont.h" +#include "../render/IRenderHandler.h" #include "../render/Graphics.h" #include "../windows/CMessage.h" #include "../windows/InfoWindows.h" @@ -95,9 +96,11 @@ void CComponent::init(ComponentType Type, ComponentSubType Subtype, std::optiona max = 80; std::vector textLines = CMessage::breakText(getSubtitle(), std::max(max, pos.w), font); + const auto & fontPtr = GH.renderHandler().loadFont(font); + const int height = static_cast(fontPtr->getLineHeight()); + for(auto & line : textLines) { - int height = static_cast(graphics->fonts[font]->getLineHeight()); auto label = std::make_shared(pos.w/2, pos.h + height/2, font, ETextAlignment::CENTER, Colors::WHITE, line); pos.h += height; diff --git a/client/widgets/CTextInput.cpp b/client/widgets/CTextInput.cpp index c138b6505..723d89a6a 100644 --- a/client/widgets/CTextInput.cpp +++ b/client/widgets/CTextInput.cpp @@ -17,6 +17,7 @@ #include "../gui/Shortcut.h" #include "../render/Graphics.h" #include "../render/IFont.h" +#include "../render/IRenderHandler.h" #include "../../lib/texts/TextOperations.h" @@ -190,8 +191,9 @@ void CTextInput::updateLabel() std::string visibleText = getVisibleText(); label->alignment = originalAlignment; + const auto & font = GH.renderHandler().loadFont(label->font); - while (graphics->fonts[label->font]->getStringWidth(visibleText) > pos.w) + while (font->getStringWidth(visibleText) > pos.w) { label->alignment = ETextAlignment::CENTERRIGHT; visibleText = visibleText.substr(TextOperations::getUnicodeCharacterSize(visibleText[0])); diff --git a/client/widgets/TextControls.cpp b/client/widgets/TextControls.cpp index c2d1f8cb0..8975fd469 100644 --- a/client/widgets/TextControls.cpp +++ b/client/widgets/TextControls.cpp @@ -22,6 +22,7 @@ #include "../render/Canvas.h" #include "../render/Graphics.h" #include "../render/IFont.h" +#include "../render/IRenderHandler.h" #include "../../lib/texts/TextOperations.h" @@ -56,8 +57,9 @@ CLabel::CLabel(int x, int y, EFonts Font, ETextAlignment Align, const ColorRGBA if(alignment == ETextAlignment::TOPLEFT) // causes issues for MIDDLE { - pos.w = (int)graphics->fonts[font]->getStringWidth(visibleText().c_str()); - pos.h = (int)graphics->fonts[font]->getLineHeight(); + const auto & fontPtr = GH.renderHandler().loadFont(font); + pos.w = fontPtr->getStringWidth(visibleText().c_str()); + pos.h = fontPtr->getLineHeight(); } } @@ -114,8 +116,12 @@ void CLabel::setMaxWidth(int width) void CLabel::trimText() { if(maxWidth > 0) - while ((int)graphics->fonts[font]->getStringWidth(visibleText().c_str()) > maxWidth) + { + const auto & fontPtr = GH.renderHandler().loadFont(font); + + while (fontPtr->getStringWidth(visibleText().c_str()) > maxWidth) TextOperations::trimRightUnicode(text); + } } void CLabel::setColor(const ColorRGBA & Color) @@ -132,7 +138,8 @@ void CLabel::setColor(const ColorRGBA & Color) size_t CLabel::getWidth() { - return graphics->fonts[font]->getStringWidth(visibleText()); + const auto & fontPtr = GH.renderHandler().loadFont(font); + return fontPtr->getStringWidth(visibleText()); } CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, ETextAlignment Align, const ColorRGBA & Color, const std::string & Text) : @@ -171,7 +178,7 @@ void CMultiLineLabel::setText(const std::string & Txt) void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what) { - const auto f = graphics->fonts[font]; + const auto f = GH.renderHandler().loadFont(font); Point where = destRect.topLeft(); const std::string delimiters = "{}"; auto delimitersCount = std::count_if(what.cbegin(), what.cend(), [&delimiters](char c) @@ -264,7 +271,7 @@ void CMultiLineLabel::showAll(Canvas & to) { CIntObject::showAll(to); - const auto f = graphics->fonts[font]; + const auto & fontPtr = GH.renderHandler().loadFont(font); // calculate which lines should be visible int totalLines = static_cast(lines.size()); @@ -274,17 +281,17 @@ void CMultiLineLabel::showAll(Canvas & to) if(beginLine < 0) beginLine = 0; else - beginLine /= (int)f->getLineHeight(); + beginLine /= fontPtr->getLineHeight(); if(endLine < 0) endLine = 0; else - endLine /= (int)f->getLineHeight(); + endLine /= fontPtr->getLineHeight(); endLine++; // and where they should be displayed - Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * (int)f->getLineHeight()); - Point lineSize = Point(getTextLocation().w, (int)f->getLineHeight()); + Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * fontPtr->getLineHeight()); + Point lineSize = Point(getTextLocation().w, fontPtr->getLineHeight()); CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), getTextLocation()); // to properly trim text that is too big to fit @@ -293,7 +300,7 @@ void CMultiLineLabel::showAll(Canvas & to) if(!lines[i].empty()) //non-empty line blitLine(to, Rect(lineStart, lineSize), lines[i]); - lineStart.y += (int)f->getLineHeight(); + lineStart.y += fontPtr->getLineHeight(); } } @@ -301,15 +308,15 @@ void CMultiLineLabel::splitText(const std::string & Txt, bool redrawAfter) { lines.clear(); - const auto f = graphics->fonts[font]; - int lineHeight = static_cast(f->getLineHeight()); + const auto & fontPtr = GH.renderHandler().loadFont(font); + int lineHeight = static_cast(fontPtr->getLineHeight()); lines = CMessage::breakText(Txt, pos.w, font); textSize.y = lineHeight * (int)lines.size(); textSize.x = 0; for(const std::string & line : lines) - vstd::amax(textSize.x, f->getStringWidth(line.c_str())); + vstd::amax(textSize.x, fontPtr->getStringWidth(line.c_str())); if(redrawAfter) redraw(); } @@ -322,7 +329,8 @@ Rect CMultiLineLabel::getTextLocation() if(pos.h <= textSize.y) return pos; - Point textSize(pos.w, (int)graphics->fonts[font]->getLineHeight() * (int)lines.size()); + const auto & fontPtr = GH.renderHandler().loadFont(font); + Point textSize(pos.w, fontPtr->getLineHeight() * (int)lines.size()); Point textOffset(pos.w - textSize.x, pos.h - textSize.y); switch(alignment) @@ -421,11 +429,12 @@ void CTextBox::setText(const std::string & text) label->pos.w = pos.w - 16; assert(label->pos.w > 0); label->setText(text); + const auto & fontPtr = GH.renderHandler().loadFont(label->font); OBJECT_CONSTRUCTION; slider = std::make_shared(Point(pos.w - 16, 0), pos.h, std::bind(&CTextBox::sliderMoved, this, _1), label->pos.h, label->textSize.y, 0, Orientation::VERTICAL, CSlider::EStyle(sliderStyle)); - slider->setScrollStep((int)graphics->fonts[label->font]->getLineHeight()); + slider->setScrollStep(fontPtr->getLineHeight()); slider->setPanningStep(1); slider->setScrollBounds(pos - slider->pos.topLeft()); } diff --git a/client/windows/CHeroBackpackWindow.cpp b/client/windows/CHeroBackpackWindow.cpp index 9f8147c86..361350a72 100644 --- a/client/windows/CHeroBackpackWindow.cpp +++ b/client/windows/CHeroBackpackWindow.cpp @@ -77,6 +77,7 @@ CHeroQuickBackpackWindow::CHeroQuickBackpackWindow(const CGHeroInstance * hero, addSet(arts); arts->setHero(hero); addUsedEvents(GESTURE); + addUsedEvents(LCLICK); pos.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin; pos.h = stretchedBackground->pos.h = arts->pos.h + windowMargin; } @@ -96,6 +97,11 @@ void CHeroQuickBackpackWindow::gesturePanning(const Point & initialPosition, con redraw(); } +void CHeroQuickBackpackWindow::notFocusedClick() +{ + close(); +} + void CHeroQuickBackpackWindow::showAll(Canvas & to) { if(arts->getSlotsNum() == 0) diff --git a/client/windows/CHeroBackpackWindow.h b/client/windows/CHeroBackpackWindow.h index aff8cbca9..5dcd2ab1c 100644 --- a/client/windows/CHeroBackpackWindow.h +++ b/client/windows/CHeroBackpackWindow.h @@ -33,6 +33,7 @@ public: CHeroQuickBackpackWindow(const CGHeroInstance * hero, ArtifactPosition targetSlot); void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override; void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override; + void notFocusedClick() override; private: std::shared_ptr arts; diff --git a/client/windows/CMessage.cpp b/client/windows/CMessage.cpp index be7c3373c..51c0da65c 100644 --- a/client/windows/CMessage.cpp +++ b/client/windows/CMessage.cpp @@ -70,6 +70,8 @@ std::vector CMessage::breakText(std::string text, size_t maxLineWid boost::algorithm::trim_right_if(text, boost::algorithm::is_any_of(std::string(" "))); + const auto & fontPtr = GH.renderHandler().loadFont(font); + // each iteration generates one output line while(text.length()) { @@ -83,7 +85,7 @@ std::vector CMessage::breakText(std::string text, size_t maxLineWid std::string printableString; // loops till line is full or end of text reached - while(currPos < text.length() && text[currPos] != 0x0a && graphics->fonts[font]->getStringWidth(printableString) <= maxLineWidth) + while(currPos < text.length() && text[currPos] != 0x0a && fontPtr->getStringWidth(printableString) <= maxLineWidth) { symbolSize = TextOperations::getUnicodeCharacterSize(text[currPos]); @@ -193,9 +195,9 @@ std::string CMessage::guessHeader(const std::string & msg) int CMessage::guessHeight(const std::string & txt, int width, EFonts font) { - const auto f = graphics->fonts[font]; + const auto & fontPtr = GH.renderHandler().loadFont(font); const auto lines = CMessage::breakText(txt, width, font); - size_t lineHeight = f->getLineHeight(); + size_t lineHeight = fontPtr->getLineHeight(); return lineHeight * lines.size(); } diff --git a/client/windows/CWindowWithArtifacts.cpp b/client/windows/CWindowWithArtifacts.cpp index 24daa3d1c..0cd4f7032 100644 --- a/client/windows/CWindowWithArtifacts.cpp +++ b/client/windows/CWindowWithArtifacts.cpp @@ -86,6 +86,11 @@ void CWindowWithArtifacts::clickPressedOnArtPlace(const CGHeroInstance * hero, c if(allowExchange || hero->id == heroArtOwner->id) putPickedArtifact(*hero, slot); } + else if(GH.isKeyboardShiftDown()) + { + if(ArtifactUtils::isSlotEquipment(slot)) + GH.windows().createAndPushWindow(hero, slot); + } else if(auto art = hero->getArt(slot)) { if(hero->getOwner() == LOCPLINT->playerID) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index ea9e7a77f..46a9775e5 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -34,6 +34,7 @@ #include "../widgets/TextControls.h" #include "../widgets/ObjectLists.h" #include "../widgets/VideoWidget.h" +#include "../widgets/GraphicalPrimitiveCanvas.h" #include "../render/Canvas.h" #include "../render/IRenderHandler.h" @@ -1616,22 +1617,26 @@ VideoWindow::VideoWindow(VideoPath video, ImagePath rim, bool showBackground, fl addUsedEvents(LCLICK | KEYBOARD); + if(showBackground) + backgroundAroundWindow = std::make_shared(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, GH.screenDimensions().x, GH.screenDimensions().y)); + if(!rim.empty()) { + setBackground(rim); videoPlayer = std::make_shared(Point(80, 186), video, true, [this](){ exit(false); }); pos = center(Rect(0, 0, 800, 600)); } else { + blackBackground = std::make_shared(Rect(0, 0, GH.screenDimensions().x, GH.screenDimensions().y)); videoPlayer = std::make_shared(Point(0, 0), video, true, scaleFactor, [this](){ exit(false); }); pos = center(Rect(0, 0, videoPlayer->pos.w, videoPlayer->pos.h)); + blackBackground->addBox(Point(0, 0), Point(pos.x, pos.y), Colors::BLACK); } - if(showBackground) - backgroundAroundWindow = std::make_shared(ImagePath::builtin("DIBOXBCK"), Rect(-pos.x, -pos.y, GH.screenDimensions().x, GH.screenDimensions().y)); + if(backgroundAroundWindow) + backgroundAroundWindow->pos.moveTo(Point(0, 0)); - if(!rim.empty()) - setBackground(rim); } void VideoWindow::exit(bool skipped) diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 4c7b68a75..5e58ad6d2 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -44,6 +44,7 @@ class CFilledTexture; class IImage; class VideoWidget; class VideoWidgetOnce; +class GraphicalPrimitiveCanvas; enum class EUserEvent; @@ -506,6 +507,7 @@ class VideoWindow : public CWindowObject { std::shared_ptr videoPlayer; std::shared_ptr backgroundAroundWindow; + std::shared_ptr blackBackground; std::function closeCb; diff --git a/config/campaignOverrides.json b/config/campaignOverrides.json index 28bbbfca6..45b11877c 100644 --- a/config/campaignOverrides.json +++ b/config/campaignOverrides.json @@ -6,7 +6,7 @@ "introVideo": "H3X1intr", "videoRim": "IntroRm2" }, - "MAPS/HC1_MAIN" : { // Heroes Chronicles 1 + "MAPS/CHRONICLES/HC1_MAIN" : { // Heroes Chronicles 1 "regions": { "background": "chronicles_1/CamBkHc", @@ -14,14 +14,14 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 27, "y": 43 }, - { "infix": "2", "x": 231, "y": 43 }, - { "infix": "3", "x": 27, "y": 178 }, - { "infix": "4", "x": 231, "y": 178 }, - { "infix": "5", "x": 27, "y": 312 }, - { "infix": "6", "x": 231, "y": 312 }, - { "infix": "7", "x": 27, "y": 447 }, - { "infix": "8", "x": 231, "y": 447 } + { "infix": "1", "x": 27, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 231, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 27, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 231, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 27, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "6", "x": 231, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "7", "x": 27, "y": 447, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "8", "x": 231, "y": 447, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 8, @@ -39,7 +39,7 @@ "videoRim": "chronicles_1/INTRORIM", "introVideo": "chronicles_1/Intro" }, - "MAPS/HC2_MAIN" : { // Heroes Chronicles 2 + "MAPS/CHRONICLES/HC2_MAIN" : { // Heroes Chronicles 2 "regions": { "background": "chronicles_2/CamBkHc", @@ -47,14 +47,14 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 27, "y": 43 }, - { "infix": "2", "x": 231, "y": 43 }, - { "infix": "3", "x": 27, "y": 178 }, - { "infix": "4", "x": 231, "y": 178 }, - { "infix": "5", "x": 27, "y": 312 }, - { "infix": "6", "x": 231, "y": 312 }, - { "infix": "7", "x": 27, "y": 447 }, - { "infix": "8", "x": 231, "y": 447 } + { "infix": "1", "x": 27, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 231, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 27, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 231, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 27, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "6", "x": 231, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "7", "x": 27, "y": 447, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "8", "x": 231, "y": 447, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 8, @@ -72,7 +72,7 @@ "videoRim": "chronicles_2/INTRORIM", "introVideo": "chronicles_2/Intro" }, - "MAPS/HC3_MAIN" : { // Heroes Chronicles 3 + "MAPS/CHRONICLES/HC3_MAIN" : { // Heroes Chronicles 3 "regions": { "background": "chronicles_3/CamBkHc", @@ -80,14 +80,14 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 27, "y": 43 }, - { "infix": "2", "x": 231, "y": 43 }, - { "infix": "3", "x": 27, "y": 178 }, - { "infix": "4", "x": 231, "y": 178 }, - { "infix": "5", "x": 27, "y": 312 }, - { "infix": "6", "x": 231, "y": 312 }, - { "infix": "7", "x": 27, "y": 447 }, - { "infix": "8", "x": 231, "y": 447 } + { "infix": "1", "x": 27, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 231, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 27, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 231, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 27, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "6", "x": 231, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "7", "x": 27, "y": 447, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "8", "x": 231, "y": 447, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 8, @@ -105,7 +105,7 @@ "videoRim": "chronicles_3/INTRORIM", "introVideo": "chronicles_3/Intro" }, - "MAPS/HC4_MAIN" : { // Heroes Chronicles 4 + "MAPS/CHRONICLES/HC4_MAIN" : { // Heroes Chronicles 4 "regions": { "background": "chronicles_4/CamBkHc", @@ -113,14 +113,14 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 27, "y": 43 }, - { "infix": "2", "x": 231, "y": 43 }, - { "infix": "3", "x": 27, "y": 178 }, - { "infix": "4", "x": 231, "y": 178 }, - { "infix": "5", "x": 27, "y": 312 }, - { "infix": "6", "x": 231, "y": 312 }, - { "infix": "7", "x": 27, "y": 447 }, - { "infix": "8", "x": 231, "y": 447 } + { "infix": "1", "x": 27, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 231, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 27, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 231, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 27, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "6", "x": 231, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "7", "x": 27, "y": 447, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "8", "x": 231, "y": 447, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 8, @@ -138,7 +138,7 @@ "videoRim": "chronicles_4/INTRORIM", "introVideo": "chronicles_4/Intro" }, - "MAPS/HC5_MAIN" : { // Heroes Chronicles 5 + "MAPS/CHRONICLES/HC5_MAIN" : { // Heroes Chronicles 5 "regions": { "background": "chronicles_5/CamBkHc", @@ -146,11 +146,11 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 34, "y": 184 }, - { "infix": "2", "x": 235, "y": 184 }, - { "infix": "3", "x": 34, "y": 320 }, - { "infix": "4", "x": 235, "y": 320 }, - { "infix": "5", "x": 129, "y": 459 } + { "infix": "1", "x": 34, "y": 184, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 235, "y": 184, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 34, "y": 320, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 235, "y": 320, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 129, "y": 459, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 5, @@ -165,7 +165,7 @@ "videoRim": "chronicles_5/INTRORIM", "introVideo": "chronicles_5/Intro" }, - "MAPS/HC6_MAIN" : { // Heroes Chronicles 6 + "MAPS/CHRONICLES/HC6_MAIN" : { // Heroes Chronicles 6 "regions": { "background": "chronicles_6/CamBkHc", @@ -173,11 +173,11 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 34, "y": 184 }, - { "infix": "2", "x": 235, "y": 184 }, - { "infix": "3", "x": 34, "y": 320 }, - { "infix": "4", "x": 235, "y": 320 }, - { "infix": "5", "x": 129, "y": 459 } + { "infix": "1", "x": 34, "y": 184, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 235, "y": 184, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 34, "y": 320, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 235, "y": 320, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 129, "y": 459, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 5, @@ -192,7 +192,7 @@ "videoRim": "chronicles_6/INTRORIM", "introVideo": "chronicles_6/Intro" }, - "MAPS/HC7_MAIN" : { // Heroes Chronicles 7 + "MAPS/CHRONICLES/HC7_MAIN" : { // Heroes Chronicles 7 "regions": { "background": "chronicles_7/CamBkHc", @@ -200,14 +200,14 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 27, "y": 43 }, - { "infix": "2", "x": 231, "y": 43 }, - { "infix": "3", "x": 27, "y": 178 }, - { "infix": "4", "x": 231, "y": 178 }, - { "infix": "5", "x": 27, "y": 312 }, - { "infix": "6", "x": 231, "y": 312 }, - { "infix": "7", "x": 27, "y": 447 }, - { "infix": "8", "x": 231, "y": 447 } + { "infix": "1", "x": 27, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 231, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 27, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 231, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 27, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "6", "x": 231, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "7", "x": 27, "y": 447, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "8", "x": 231, "y": 447, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 8, @@ -225,7 +225,7 @@ "videoRim": "chronicles_7/INTRORIM", "introVideo": "chronicles_7/Intro5" }, - "MAPS/HC8_MAIN" : { // Heroes Chronicles 8 + "MAPS/CHRONICLES/HC8_MAIN" : { // Heroes Chronicles 8 "regions": { "background": "chronicles_8/CamBkHc", @@ -233,14 +233,14 @@ "suffix": ["1", "2", "3"], "color_suffix_length": 0, "desc": [ - { "infix": "1", "x": 27, "y": 43 }, - { "infix": "2", "x": 231, "y": 43 }, - { "infix": "3", "x": 27, "y": 178 }, - { "infix": "4", "x": 231, "y": 178 }, - { "infix": "5", "x": 27, "y": 312 }, - { "infix": "6", "x": 231, "y": 312 }, - { "infix": "7", "x": 27, "y": 447 }, - { "infix": "8", "x": 231, "y": 447 } + { "infix": "1", "x": 27, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "2", "x": 231, "y": 43, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "3", "x": 27, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "4", "x": 231, "y": 178, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "5", "x": 27, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "6", "x": 231, "y": 312, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "7", "x": 27, "y": 447, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "8", "x": 231, "y": 447, "labelPos": { "x": 98, "y": 112 } } ] }, "scenarioCount": 8, diff --git a/config/creatures/castle.json b/config/creatures/castle.json index a88d5a115..e45c185db 100644 --- a/config/creatures/castle.json +++ b/config/creatures/castle.json @@ -10,7 +10,7 @@ "cavalryChargeImmunity" : { "type" : "CHARGE_IMMUNITY" - }, + } }, "graphics" : { diff --git a/config/fonts.json b/config/fonts.json index 64c9ad760..0ba3f076f 100644 --- a/config/fonts.json +++ b/config/fonts.json @@ -5,12 +5,12 @@ "bitmap" : [ "BIGFONT", // Mostly used for window titles - "CALLI10R", // Unused in VCMI - "CREDITS", // Used for credits menu - "HISCORE", // Unused in VCMI + "CALLI10R", // Only in World View menu + "CREDITS", // Only in Credits menu + "HISCORE", // Only in High Scores menu "MEDFONT", // Some titles "SMALFONT", // Most of the messages - "TIMES08R", // Used to display amounts on creature card + "TIMES08R", // Unused in VCMI "TINY", // Some text "VERD10B" // Unused in VCMI ], @@ -25,17 +25,18 @@ // b) list of scaling factors for each scaling mode, e.g. [ 10, 16, 22, 26]. In this case game will select point size according to xBRZ scaling factor // so unscaled mode will use 10px, xbrz2 will use 16px, and xbrz3 will use 22 // "style" - italic and\or bold, indicates font style - // "blend" - if set to true, font will be antialiased + // "outline" - if set, black shadow will be generated around entire text (instead of only bottom-right side) + // "noShadow" - if set, this font will not drop any shadow "trueType": { - //"BIGFONT" : { "file" : "LiberationSerif-Bold.ttf", "size" : 22, "blend" : true}, - //"CALLI10R" : { "file" : "Georgia.ttf", "size" : 10}, - //"CREDITS" : { "file" : "LiberationSerif-Bold.ttf", "size" : 28}, - //"HISCORE" : { "file" : "Georgia.ttf", "size" : 13}, - //"MEDFONT" : { "file" : "LiberationSerif-Bold.ttf", "size" : 16}, // breaks messages (from map events) - //"SMALFONT" : { "file" : "LiberationSerif-Regular.ttf", "size" : 13, "blend" : true}, - //"TIMES08R" : { "file" : "LiberationSerif-Regular.ttf", "size" : 11, "blend" : true}, - //"TINY" : { "file" : "LiberationSerif-Regular.ttf", "size" : 11, "blend" : true}, - //"VERD10B" : { "file" : "Georgia.ttf", "size" : 13} + "BIGFONT" : { "file" : "NotoSerif-Bold.ttf", "size" : [ 19, 39, 58, 78] }, + "CALLI10R" : { "file" : "NotoSerif-Bold.ttf", "size" : [ 12, 24, 36, 48] }, // TODO: find better matching font? This is likely non-free 'Callisto MT' font + "CREDITS" : { "file" : "NotoSerif-Black.ttf", "size" : [ 22, 44, 66, 88], "outline" : true }, + "HISCORE" : { "file" : "NotoSerif-Black.ttf", "size" : [ 18, 36, 54, 72], "outline" : true }, + "MEDFONT" : { "file" : "NotoSerif-Bold.ttf", "size" : [ 15, 31, 46, 62] }, + "SMALFONT" : { "file" : "NotoSerif-Medium.ttf", "size" : [ 12, 24, 36, 48] }, + "TIMES08R" : { "file" : "NotoSerif-Medium.ttf", "size" : [ 8, 16, 24, 32] }, + "TINY" : { "file" : "NotoSans-Medium.ttf", "size" : [ 9, 19, 28, 38], "noShadow" : true }, // The only H3 font without shadow + "VERD10B" : { "file" : "NotoSans-Medium.ttf", "size" : [ 13, 26, 39, 52] } } } diff --git a/config/gameConfig.json b/config/gameConfig.json index b57bb849e..714309b2b 100644 --- a/config/gameConfig.json +++ b/config/gameConfig.json @@ -335,6 +335,8 @@ "defensePointDamageFactorCap": 0.7, // If set to true, double-wide creatures will trigger obstacle effect when moving one tile forward or backwards "oneHexTriggersObstacles": false, + // Allow area shooters with SPELL_LIKE_ATTACK bonus such as liches or magogs to target empty hexes + "areaShotCanTargetEmptyHex" : false, // Positions of units on start of the combat // If battle does not defines specific configuration, 'default' configuration will be used diff --git a/config/schemas/gameSettings.json b/config/schemas/gameSettings.json index 64e75dac2..0eec56189 100644 --- a/config/schemas/gameSettings.json +++ b/config/schemas/gameSettings.json @@ -69,7 +69,8 @@ "defensePointDamageFactor" : { "type" : "number" }, "defensePointDamageFactorCap" : { "type" : "number" }, "oneHexTriggersObstacles" : { "type" : "boolean" }, - "layouts" : { "type" : "object" } + "layouts" : { "type" : "object" }, + "areaShotCanTargetEmptyHex" : { "type" : "boolean" } } }, "creatures": { diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 24e353ee0..3cc99dc28 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -166,6 +166,8 @@ "showfps", "targetfps", "vsync", + "fontsType", + "fontScalingFactor", "upscalingFilter", "fontUpscalingFilter", "downscalingFilter" @@ -232,6 +234,15 @@ "type" : "boolean", "default" : true }, + "fontsType" : { + "type" : "string", + "enum" : [ "auto", "original", "scalable" ], + "default" : "auto" + }, + "fontScalingFactor" : { + "type" : "number", + "default" : 1 + }, "fontUpscalingFilter" : { "type" : "string", "enum" : [ "nearest", "bilinear", "xbrz" ], diff --git a/docs/modders/Campaign_Format.md b/docs/modders/Campaign_Format.md index 4711361ea..9fec41be2 100644 --- a/docs/modders/Campaign_Format.md +++ b/docs/modders/Campaign_Format.md @@ -191,9 +191,9 @@ Predefined campaign regions are located in file `campaign_regions.json` "prefix": "G3", "colorSuffixLength": 1, "desc": [ - { "infix": "A", "x": 289, "y": 376 }, - { "infix": "B", "x": 60, "y": 147 }, - { "infix": "C", "x": 131, "y": 202 } + { "infix": "A", "x": 289, "y": 376, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "B", "x": 60, "y": 147, "labelPos": { "x": 98, "y": 112 } }, + { "infix": "C", "x": 131, "y": 202, "labelPos": { "x": 98, "y": 112 } } ] }, ``` @@ -202,6 +202,7 @@ Predefined campaign regions are located in file `campaign_regions.json` - `"prefix"` used to identify all images related to campaign. In this example (if background parameter wouldn't exists), background picture will be `G3_BG` - `"suffix"` optional - use other suffixes than the default `En`, `Se` and `Co` for the three different images - `"infix"` used to identify all images related to region. In this example, it will be pictures whose files names begin with `G3A_..., G3B_..., G3C_..."` +- `"labelPos"` optional - to add scenario name as label on map - `"colorSuffixLength"` identifies suffix length for region colourful frames. 0 is no color suffix (no colorisation), 1 is used for `R, B, N, G, O, V, T, P`, value 2 is used for `Re, Bl, Br, Gr, Or, Vi, Te, Pi` ## Packing campaign diff --git a/launcher/modManager/chroniclesextractor.cpp b/launcher/modManager/chroniclesextractor.cpp index 08a3afe8a..57759e2df 100644 --- a/launcher/modManager/chroniclesextractor.cpp +++ b/launcher/modManager/chroniclesextractor.cpp @@ -168,7 +168,7 @@ void ChroniclesExtractor::extractFiles(int no) const QDir outDirSprites(pathToQString(basePath / "Sprites" / chroniclesDir)); QDir outDirVideo(pathToQString(basePath / "Video" / chroniclesDir)); QDir outDirSounds(pathToQString(basePath / "Sounds" / chroniclesDir)); - QDir outDirMaps(pathToQString(basePath / "Maps")); + QDir outDirMaps(pathToQString(basePath / "Maps" / "Chronicles")); auto extract = [](QDir scrDir, QDir dest, QString file, std::vector files = {}){ CArchiveLoader archive("", scrDir.filePath(scrDir.entryList({file}).front()).toStdString(), false); diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index bc9a7776f..8cb8c32a0 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/launcher/settingsView/csettingsview_moc.cpp @@ -174,17 +174,13 @@ void CSettingsView::loadSettings() ui->sliderControllerSticksAcceleration->setValue(settings["input"]["controllerAxisScale"].Float() * 100); ui->lineEditGameLobbyHost->setText(QString::fromStdString(settings["lobby"]["hostname"].String())); ui->spinBoxNetworkPortLobby->setValue(settings["lobby"]["port"].Integer()); - - auto mainWindow = getMainWindow(); - if(mainWindow) - { - bool fontModAvailable = mainWindow->getModView()->isModInstalled("vcmi-extras.truetypefonts"); - if(!fontModAvailable) - { - ui->labelTtfFont->hide(); - ui->buttonTtfFont->hide(); - } - } + + if (settings["video"]["fontsType"].String() == "auto") + ui->buttonFontAuto->setChecked(true); + else if (settings["video"]["fontsType"].String() == "original") + ui->buttonFontOriginal->setChecked(true); + else + ui->buttonFontScalable->setChecked(true); loadToggleButtonSettings(); } @@ -210,9 +206,9 @@ void CSettingsView::loadToggleButtonSettings() int cursorTypeIndex = vstd::find_pos(cursorTypesList, cursorType); setCheckbuttonState(ui->buttonCursorType, cursorTypeIndex); - auto mainWindow = getMainWindow(); - if(mainWindow) - setCheckbuttonState(ui->buttonTtfFont, mainWindow->getModView()->isModEnabled("vcmi-extras.truetypefonts")); + int fontScalingPercentage = settings["video"]["fontScalingFactor"].Float() * 100; + ui->sliderScalingFont->setValue(fontScalingPercentage / 5); + } void CSettingsView::fillValidResolutions() @@ -770,12 +766,28 @@ void CSettingsView::on_sliderControllerSticksSensitivity_valueChanged(int value) node->Integer() = value; } -void CSettingsView::on_buttonTtfFont_toggled(bool value) +void CSettingsView::on_sliderScalingFont_valueChanged(int value) { - auto mainWindow = getMainWindow(); - if(value) - mainWindow->getModView()->enableModByName("vcmi-extras.truetypefonts"); - else - mainWindow->getModView()->disableModByName("vcmi-extras.truetypefonts"); - updateCheckbuttonText(ui->buttonTtfFont); + int actualValuePercentage = value * 5; + ui->labelScalingFontValue->setText(QString("%1%").arg(actualValuePercentage)); + Settings node = settings.write["video"]["fontScalingFactor"]; + node->Float() = actualValuePercentage / 100.0; +} + +void CSettingsView::on_buttonFontAuto_clicked(bool checked) +{ + Settings node = settings.write["video"]["fontsType"]; + node->String() = "auto"; +} + +void CSettingsView::on_buttonFontScalable_clicked(bool checked) +{ + Settings node = settings.write["video"]["fontsType"]; + node->String() = "scalable"; +} + +void CSettingsView::on_buttonFontOriginal_clicked(bool checked) +{ + Settings node = settings.write["video"]["fontsType"]; + node->String() = "original"; } diff --git a/launcher/settingsView/csettingsview_moc.h b/launcher/settingsView/csettingsview_moc.h index 4bcf51ad9..1e76f6f2e 100644 --- a/launcher/settingsView/csettingsview_moc.h +++ b/launcher/settingsView/csettingsview_moc.h @@ -88,7 +88,13 @@ private slots: void on_sliderControllerSticksSensitivity_valueChanged(int value); - void on_buttonTtfFont_toggled(bool value); + //void on_buttonTtfFont_toggled(bool value); + + void on_sliderScalingFont_valueChanged(int value); + + void on_buttonFontAuto_clicked(bool checked); + void on_buttonFontScalable_clicked(bool checked); + void on_buttonFontOriginal_clicked(bool checked); private: Ui::CSettingsView * ui; diff --git a/launcher/settingsView/csettingsview_moc.ui b/launcher/settingsView/csettingsview_moc.ui index 87e8f8e70..578fd55b4 100644 --- a/launcher/settingsView/csettingsview_moc.ui +++ b/launcher/settingsView/csettingsview_moc.ui @@ -49,27 +49,12 @@ 0 0 729 - 1396 + 1449 - - - - - - true - - - - General - - - 5 - - - + - + false @@ -88,76 +73,8 @@ - - - - Sticks Acceleration - - - - - - - Refresh now - - - - - - - - - - - - - - % - - - 50 - - - 400 - - - 10 - - - - - - - Reserved screen area - - - - - - - Online Lobby address - - - - - - - VCAI - - - - VCAI - - - - - Nullkiller - - - - - - + + 0 @@ -172,42 +89,10 @@ - - - - 100 - - - Qt::Horizontal - - - QSlider::TicksAbove - - - 10 - - - - - - - - 0 - 0 - - + + - - - - true - - - - - - - VCMI Language + Use Relative Pointer Mode @@ -227,39 +112,105 @@ - - - - - 0 - 0 - - + + - - - - true + Additional repository - - + + - Default repository + Music Volume - - + + + + + - 1024 + 0 - 65535 + 50 + + + 1 + + + 10 - 3030 + 0 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + 10 + + + + + + + Long Touch Duration + + + + + + + false + + + BattleAI + + + + BattleAI + + + + + StupidAI + + + + + + + + + true + + + + Video + + + 5 + + + + + + + + true + + + + General + + + 5 @@ -270,119 +221,34 @@ - - - - 100 - - - 300 - - - Qt::Horizontal - - - QSlider::TicksAbove - - - 25 - - - - - - - Renderer - - - - - - - - true - - - - Artificial Intelligence - - - 5 - - - - + - Use Relative Pointer Mode + Show Tutorial again - - + + - Fullscreen + Online Lobby port - - + + - Autocombat AI in battles + Relative Pointer Speed - - - - Display index - - - - - - - Long Touch Duration - - - - - - - Sticks Sensitivity - - - - + Touch Tap Tolerance - - - - - - - - - - - 100 - - - Qt::Horizontal - - - QSlider::TicksAbove - - - 10 - - - @@ -414,168 +280,20 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - - - true - - - - 0 - 0 - - - - - - - true - - - false - - - - - - - - - - true - - - - - - - Show intro - - - - - - - - - - 0 - - - 50 - - - 1 - - - 10 - - - 0 - - - Qt::Horizontal - - - QSlider::TicksAbove - - - 10 - - - - - - - Online Lobby port - - - - - - - - 0 - 0 - - - - - - - true - - - - - - - - - - - true - - - - Video - - - 5 - - - - - - - - true - - - - Audio - - - 5 - - - - - - - - 0 - 0 - - - - - - - true - - - false - - - - - + + 500 - 2500 + 2000 - 25 + 250 250 - - 500 - Qt::Horizontal @@ -587,67 +305,75 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - + + - VSync + Neutral AI in battles - - + + + + + true + + - + Input - Mouse + + + 5 - - - - - Nearest - - - - - Linear - - - - - Automatic (Linear) - - + + + + + 0 + 0 + + + + Automatic + + + true + + + true + + + buttonGroup + - - - - false + + + + Adventure Map Enemies + + + + - BattleAI + VCAI - BattleAI + VCAI - StupidAI + Nullkiller - - - - Check on startup - - - - + 0 @@ -675,35 +401,38 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - - - - 0 - 0 - - + + - - - - true + Autosave - - - - 500 + + + + Renderer + + + + + + Autosave limit (0 = off) + + + + + + + Default repository + + + + + - 2000 - - - 250 - - - 250 + 100 Qt::Horizontal @@ -712,15 +441,44 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use QSlider::TicksAbove - 250 + 10 - - + + + + Sticks Sensitivity + + - - + + + + + true + + + + Audio + + + 5 + + + + + + + + + + Downscaling Filter + + + + + 1024 @@ -732,222 +490,56 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - - - Enemy AI in battles + + + + 0 + + + 50 + + + 1 + + + 10 + + + 0 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + 10 - - - - - true - - - - Input - Touchscreen - - - 5 - - - - - - - Autosave prefix - - - - + Ignore SSL errors - - - - - + + - Autosave limit (0 = off) + Upscaling Filter - - + + - + Refresh now - - - - Autosave - - - - - - - Haptic Feedback - - - - - - - - 0 - 0 - - - - - - - true - - - - - - - Additional repository - - - - - - - Mouse Click Tolerance - - - - - - - Software Cursor - - - - - - - Relative Pointer Speed - - - - - - - - true - - - - Network - - - 5 - - - - - - - Network port - - - - - - - true - - - - 0 - 0 - - - - - - - true - - - false - - - - - - - Downscaling Filter - - - - - - - Adventure Map Enemies - - - - - - - BattleAI - - - - BattleAI - - - - - StupidAI - - - - - - - - Neutral AI in battles - - - - - - - Adventure Map Allies - - - - - - - - true - - - - Input - Mouse - - - 5 - - - - + true @@ -969,23 +561,52 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - - - - - - 20 - - - 1000 - - - 10 + + + + VSync - + + + + Resolution + + + + + + + VCMI Language + + + + + + + Interface Scaling + + + + + + + + + + + + + + + + + Online Lobby address + + + + VCAI @@ -1002,43 +623,30 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - - - empty = map name prefix - - - - - - - - true - + + + + + 0 + 0 + - Input - Controller + - - 5 + + true - - + + - Framerate Limit + - - - - Controller Click Tolerance - - - - + 100 @@ -1066,43 +674,64 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - + + - Interface Scaling + Autosave prefix - - + + - Resolution + Reserved screen area - + Sound Volume - - - - 0 + + + + Font Scaling (experimental) + + + + + + Framerate Limit + + + + + + + + Nearest + + + + + Linear + + + + + Automatic (Linear) + + + + + + - 50 - - - 1 - - - 10 - - - 0 + 100 Qt::Horizontal @@ -1115,50 +744,6 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - - - - 0 - 0 - - - - - - - true - - - - - - - Music Volume - - - - - - - Show Tutorial again - - - - - - - Reset - - - - - - - Upscaling Filter - - - @@ -1188,15 +773,115 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use + + + + Autocombat AI in battles + + + - + Use scalable fonts - - + + + + + + + true + + + + + + + + true + + + + Artificial Intelligence + + + 5 + + + + + + + Fullscreen + + + + + + + 500 + + + 2500 + + + 25 + + + 250 + + + 500 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + 250 + + + + + + + Enemy AI in battles + + + + + + + Sticks Acceleration + + + + + + + 100 + + + 300 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + 25 + + + + + 0 @@ -1211,6 +896,412 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use + + + + empty = map name prefix + + + + + + + true + + + + 0 + 0 + + + + + + + true + + + false + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + Adventure Map Allies + + + + + + + Haptic Feedback + + + + + + + % + + + 50 + + + 400 + + + 10 + + + + + + + + 0 + 0 + + + + + + + true + + + false + + + + + + + Mouse Click Tolerance + + + + + + + BattleAI + + + + BattleAI + + + + + StupidAI + + + + + + + + true + + + + 0 + 0 + + + + + + + true + + + false + + + + + + + Reset + + + + + + + + + + Network port + + + + + + + + + + + true + + + + Input - Controller + + + 5 + + + + + + + Display index + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + true + + + + Network + + + 5 + + + + + + + + 0 + 0 + + + + Original + + + true + + + true + + + buttonGroup + + + + + + + + + + + + + + Show intro + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + 1024 + + + 65535 + + + 3030 + + + + + + + + true + + + + Input - Touchscreen + + + 5 + + + + + + + 20 + + + 1000 + + + 10 + + + + + + + Controller Click Tolerance + + + + + + + Check on startup + + + + + + + 10 + + + 30 + + + 1 + + + 2 + + + 20 + + + 20 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + 2 + + + + + + + Software Cursor + + + + + + + + 0 + 0 + + + + Scalable + + + true + + + true + + + buttonGroup + + + + + + @@ -1219,4 +1310,7 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use + + + diff --git a/launcher/translation/chinese.ts b/launcher/translation/chinese.ts index 192d401cd..11e156178 100644 --- a/launcher/translation/chinese.ts +++ b/launcher/translation/chinese.ts @@ -235,7 +235,7 @@ - + Description 详细介绍 @@ -295,181 +295,191 @@ 终止 - + Mod name 模组名称 - + Installed version 已安装的版本 - + Latest version 最新版本 - + Size 大小 - + Download size 下载大小 - + Authors 作者 - + License 授权许可 - + Contact 联系方式 - + Compatibility 兼容性 - - + + Required VCMI version 需要VCMI版本 - + Supported VCMI version 支持的VCMI版本 - + please upgrade mod 请更新模组 - - + + mods repository index 模组源索引号 - + or newer 或更新的版本 - + Supported VCMI versions 支持的VCMI版本 - + Languages 语言 - + Required mods Mod统一翻译为模组 前置模组 - + Conflicting mods Mod统一翻译为模组 冲突的模组 - + This mod can not be installed or enabled because the following dependencies are not present 这个模组无法被安装或者激活,因为下列依赖项未满足 - + This mod can not be enabled because the following mods are incompatible with it 这个模组无法被激活,因为下列模组与其不兼容 - + This mod cannot be disabled because it is required by the following mods 这个模组无法被禁用,因为它被下列模组所依赖 - + This mod cannot be uninstalled or updated because it is required by the following mods 这个模组无法被卸载或者更新,因为它被下列模组所依赖 - + This is a submod and it cannot be installed or uninstalled separately from its parent mod 这是一个附属模组它无法在所属模组外被直接被安装或者卸载 - + Notes 笔记注释 - + All supported files 所有支持的文件格式 - + Maps 地图 - + Campaigns 战役 - + Configs 配置 - + Mods 模组 - - Select files (configs, mods, maps, campaigns) to install... - 选择文件(配置,模组,地图,战役)来安装 + + Gog files + Gog文件 - + + All files (*.*) + 所有文件 (*.*) + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + 选择需要安装的文件(配置,模组,地图,战役,gog文件)... + + + Replace config file? 替换配置文件? - + Do you want to replace %1? 你想要替换%1吗? - + Downloading %1. %p% (%v MB out of %m MB) finished 正在下载 %1. %p% (%v MB 共 %m MB) 已完成 - + Download failed 下载失败 - + Unable to download all files. Encountered errors: @@ -482,7 +492,7 @@ Encountered errors: - + Install successfully downloaded? @@ -491,34 +501,39 @@ Install successfully downloaded? 安装下载成功的部分? - + + Installing chronicles + 安装历代记 + + + Installing mod %1 正在安装模组 %1 - + Operation failed 操作失败 - + Encountered errors: 遇到问题: - + screenshots 截图 - + Screenshot %1 截图 %1 - + Mod is incompatible Mod统一翻译为模组 模组不兼容 @@ -624,7 +639,7 @@ Install successfully downloaded? CSettingsView - + Off 关闭 @@ -718,6 +733,11 @@ Install successfully downloaded? xBRZ x4 xBRZ x4 + + + Use scalable fonts + 使用可缩放字体 + Online Lobby address @@ -884,7 +904,7 @@ Install successfully downloaded? 渲染器 - + On 开启 @@ -972,31 +992,67 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use 显示开场动画 - + Active 激活 - + Disabled 禁用 - + Enable 启用 - + Not Installed 未安装 - + Install 安装 + + ChroniclesExtractor + + + File cannot opened + 无法打开文件 + + + + + Invalid file selected + 所选的文件无效 + + + + You have to select an gog installer file! + 您必须选择一个gog安装文件! + + + + You have to select an chronicle installer file! + 您必须选择一个历代记安装文件! + + + + Extracting error! + 提取错误! + + + + + + Heroes Chronicles + 英雄无敌历代记 + + File size @@ -1215,113 +1271,101 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b 追随神迹 - + Heroes III installation found! 英雄无敌3安装目录已找到! - + Copy data to VCMI folder? 复制数据到VCMI文件夹吗? - + Select %1 file... param is file extension 选择%1文件... - + You have to select %1 file! param is file extension 你必须选择%1文件! - + GOG file (*.*) GOG文件 (*.*) - + File selection 选择文件 - + File cannot be opened 打开文件失败 - + Invalid file selected 所选的文件无效 - + GOG installer GOG安装包 - + GOG data GOC数据 - + No Heroes III data! 没有英雄无敌3数据! - + Selected files do not contain Heroes III data! 所选的文件不包含英雄无敌3数据! - - - - + + + + Heroes III data not found! 未找到英雄无敌3数据! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. 从所选目录检测有效的英雄无敌3数据失败。 请选择已安装英雄无敌3的数据目录。 - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! 您提供的是GOG Galaxy安装器!这个文件不包含游戏内容,请下载离线游戏安装器! - - Stream error while extracting files! -error reason: - 提取文件时遭遇文件流错误! -错误原因: - - - - Not a supported Inno Setup installer! - 这不是一个支持的Inno Setup安装器! - - - + Extracting error! 提取错误! - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. VCMI不支持英雄无敌3高清版文件。 请选择包含《英雄无敌3:完全版》或《英雄无敌3:死亡阴影》的目录。 - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. 检测到未知或不支持的英雄无敌3版本。 @@ -1336,6 +1380,26 @@ Please select directory with Heroes III: Complete Edition or Heroes III: Shadow 图片查看器 + + Innoextract + + + Stream error while extracting files! +error reason: + 提取文件时遭遇文件流错误! +错误原因: + + + + Not a supported Inno Setup installer! + 这不是一个支持的Inno Setup安装器! + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + VCMI编译时没有启用innoextract支持,启用了才可以从exe文件中提取数据! + + Language diff --git a/launcher/translation/czech.ts b/launcher/translation/czech.ts index 462b70da0..f3a1225b1 100644 --- a/launcher/translation/czech.ts +++ b/launcher/translation/czech.ts @@ -233,7 +233,7 @@ - + Description Popis @@ -293,179 +293,189 @@ Zrušit - + Mod name Název modifikace - + Installed version Nainstalovaná verze - + Latest version Nejnovější verze - + Size Velikost - + Download size Velikost ke stažení - + Authors Autoři - + License Licence - + Contact Kontakt - + Compatibility Kompabilita - - + + Required VCMI version Vyžadovaná verze VCMI - + Supported VCMI version Podporovaná verze VCMI - + please upgrade mod prosíme aktualizujte modifikaci - - + + mods repository index index repozitáře modifikací - + or newer nebo novější - + Supported VCMI versions Podporované verze VCMI - + Languages Jazyky - + Required mods Vyžadované modifikace VCMI - + Conflicting mods Modifikace v kolizi - + This mod can not be installed or enabled because the following dependencies are not present Tato modifikace nemůže být nainstalována nebo povolena, protože následující závislosti nejsou přítomny - + This mod can not be enabled because the following mods are incompatible with it Tato modifikace nemůže být povolena, protože následující modifikace s ní nejsou kompatibilní - + This mod cannot be disabled because it is required by the following mods Tato modifikace nemůže být zakázána, protože je vyžadována následujícími modifikacemi - + This mod cannot be uninstalled or updated because it is required by the following mods Tato modifikace nemůže být odinstalována nebo aktualizována, protože je vyžadována následujícími modifikacemi - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Toto je podmodifikace, která nemůže být nainstalována nebo odinstalována bez její rodičovské modifikace - + Notes Poznámky - + All supported files Všechny podporované soubory - + Maps Mapy - + Campaigns Kampaně - + Configs Nastavení - + Mods Modifikace - - Select files (configs, mods, maps, campaigns) to install... - Vyberte soubory (nastavení, modifikace, mapy anebo kampaně) pro instalaci... + + Gog files + - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? Nahradit soubor nastavení? - + Do you want to replace %1? Chcete nahradit %1? - + Downloading %1. %p% (%v MB out of %m MB) finished - + Download failed Stahování selhalo - + Unable to download all files. Encountered errors: @@ -478,7 +488,7 @@ Vyskytly se chyby: - + Install successfully downloaded? @@ -487,34 +497,39 @@ Install successfully downloaded? Nainstalovat úspěšně stažené? - + + Installing chronicles + + + + Installing mod %1 Instalování modifikace %1 - + Operation failed Operace selhala - + Encountered errors: Vyskytly se chyby: - + screenshots snímky obrazovky - + Screenshot %1 Snímek obrazovky %1 - + Mod is incompatible Modifikace není kompatibilní @@ -619,7 +634,7 @@ Nainstalovat úspěšně stažené? CSettingsView - + Off Vypnuto @@ -713,6 +728,11 @@ Nainstalovat úspěšně stažené? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -879,7 +899,7 @@ Nainstalovat úspěšně stažené? Vykreslovač - + On Zapnuto @@ -966,31 +986,67 @@ Exkluzivní celá obrazovka - hra zakryje vaši celou obrazovku a použije vybra Zobrazit intro - + Active Aktivní - + Disabled Zakázáno - + Enable Povolit - + Not Installed Nenainstalováno - + Install Instalovat + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + Vybrán neplatný soubor + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + + + + + + + Heroes Chronicles + + + File size @@ -1208,112 +1264,101 @@ Offline instalátor obsahuje dvě části, .exe a .bin. Ujistěte se, že stahuj - + Heroes III installation found! Instalace Heroes III nalezena! - + Copy data to VCMI folder? Zkopírovat data do složky VCMI? - + Select %1 file... param is file extension Vyberte soubor %1... - + You have to select %1 file! param is file extension Musíte vybrat soubor %1! - + GOG file (*.*) GOG soubor (*.*) - + File selection Výběr souboru - + File cannot be opened - + Invalid file selected Vybrán neplatný soubor - + GOG installer Instalátor GOG - + GOG data Data GOG - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - - Stream error while extracting files! -error reason: - - - - - Not a supported Inno Setup installer! - - - - + Extracting error! - + No Heroes III data! Žádná data Heroes III! - + Selected files do not contain Heroes III data! Vybrané soubory neobsahují data Heroes III! - - - - + + + + Heroes III data not found! Data Heroes III nenalezena! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. Detekce platných dat Heroes III ve vybrané složce selhala. Prosíme vyberte složku s nainstalovanými daty Heroes III. - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Soubory Heroes III HD Edice nejsou podporována ve VCMI. Prosíme vyberte složku s Heroes III: Complete Edition nebo Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Nalezena neznámá nebo nepodporovaná verze Heroes III. @@ -1328,6 +1373,25 @@ Prosíme vyberte složku s Heroes III: Complete Edition nebo Heroes III: Shadow Prohlížeč obrázků + + Innoextract + + + Stream error while extracting files! +error reason: + + + + + Not a supported Inno Setup installer! + + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/english.ts b/launcher/translation/english.ts index 1a11f1cd1..604274ac0 100644 --- a/launcher/translation/english.ts +++ b/launcher/translation/english.ts @@ -233,7 +233,7 @@ - + Description @@ -293,179 +293,189 @@ - + Mod name - + Installed version - + Latest version - + Size - + Download size - + Authors - + License - + Contact - + Compatibility - - + + Required VCMI version - + Supported VCMI version - + please upgrade mod - - + + mods repository index - + or newer - + Supported VCMI versions - + Languages - + Required mods - + Conflicting mods - + This mod can not be installed or enabled because the following dependencies are not present - + This mod can not be enabled because the following mods are incompatible with it - + This mod cannot be disabled because it is required by the following mods - + This mod cannot be uninstalled or updated because it is required by the following mods - + This is a submod and it cannot be installed or uninstalled separately from its parent mod - + Notes - + All supported files - + Maps - + Campaigns - + Configs - + Mods - - Select files (configs, mods, maps, campaigns) to install... + + Gog files - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? - + Do you want to replace %1? - + Downloading %1. %p% (%v MB out of %m MB) finished - + Download failed - + Unable to download all files. Encountered errors: @@ -474,40 +484,45 @@ Encountered errors: - + Install successfully downloaded? - + + Installing chronicles + + + + Installing mod %1 - + Operation failed - + Encountered errors: - + screenshots - + Screenshot %1 - + Mod is incompatible @@ -611,7 +626,7 @@ Install successfully downloaded? CSettingsView - + Off @@ -700,6 +715,11 @@ Install successfully downloaded? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -871,7 +891,7 @@ Install successfully downloaded? - + On @@ -952,31 +972,67 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - + Active - + Disabled - + Enable - + Not Installed - + Install + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + + + + + + + Heroes Chronicles + + + File size @@ -1187,110 +1243,99 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b - + Heroes III installation found! - + Copy data to VCMI folder? - + Select %1 file... param is file extension - + You have to select %1 file! param is file extension - + GOG file (*.*) - + File selection - + File cannot be opened - + Invalid file selected - + GOG installer - + GOG data - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - - Stream error while extracting files! -error reason: - - - - - Not a supported Inno Setup installer! - - - - + Extracting error! - + No Heroes III data! - + Selected files do not contain Heroes III data! - - - - + + + + Heroes III data not found! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. @@ -1304,6 +1349,25 @@ Please select directory with Heroes III: Complete Edition or Heroes III: Shadow + + Innoextract + + + Stream error while extracting files! +error reason: + + + + + Not a supported Inno Setup installer! + + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/french.ts b/launcher/translation/french.ts index cb9e32807..04ab48984 100644 --- a/launcher/translation/french.ts +++ b/launcher/translation/french.ts @@ -238,7 +238,7 @@ - + Description Description @@ -293,184 +293,194 @@ Abandonner - + Mod name Nom du mod - + Installed version Version installée - + Latest version Dernière version - + Size Taille - + Download size Taille de téléchargement - + Authors Auteur(s) - + License Licence - + Contact Contact - + Compatibility Compatibilité - - + + Required VCMI version Version requise de VCMI - + Supported VCMI version Version supportée de VCMI - + please upgrade mod veuillez mettre à jour le mod - - + + mods repository index Index du dépôt de mods - + or newer ou plus récente - + Supported VCMI versions Versions supportées de VCMI - + Languages Langues - + Required mods Mods requis - + Conflicting mods Mods en conflit - + This mod can not be installed or enabled because the following dependencies are not present Ce mod ne peut pas être installé ou activé car les dépendances suivantes ne sont pas présents - + This mod can not be enabled because the following mods are incompatible with it Ce mod ne peut pas être installé ou activé, car les dépendances suivantes sont incompatibles avec lui - + This mod cannot be disabled because it is required by the following mods Ce mod ne peut pas être désactivé car il est requis pour les dépendances suivantes - + This mod cannot be uninstalled or updated because it is required by the following mods Ce mod ne peut pas être désinstallé ou mis à jour car il est requis pour les dépendances suivantes - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Ce sous-mod ne peut pas être installé ou mis à jour séparément du mod parent - + Notes Notes - + All supported files Tous les fichiers supportés - + Maps Cartes - + Campaigns Campagnes - + Configs Configurations - + Mods Mods - - Select files (configs, mods, maps, campaigns) to install... - Sélectionner les fichiers à installer (configurations, mods, cartes, campagnes)... + + Gog files + - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? Remplacer le fichier de configuration ? - + Do you want to replace %1? Voulez vous remplacer %1 ? - + Downloading %1. %p% (%v MB out of %m MB) finished Téléchargement %1. %p% (%v Mo sur %m Mo) terminé - + Download failed Téléchargement échoué - + Unable to download all files. Encountered errors: @@ -483,7 +493,7 @@ Erreur rencontrées: - + Install successfully downloaded? @@ -492,34 +502,39 @@ Install successfully downloaded? Installer les téchargements réussis? - + + Installing chronicles + + + + Installing mod %1 Installer le mod %1 - + Operation failed Opération échouée - + Encountered errors: Erreurs rencontrées: - + screenshots captures d'écran - + Screenshot %1 Impression écran %1 - + Mod is incompatible Ce mod est incompatible @@ -624,7 +639,7 @@ Installer les téchargements réussis? CSettingsView - + Off Désactivé @@ -634,7 +649,7 @@ Installer les téchargements réussis? Intelligence Artificielle - + On Activé @@ -708,6 +723,11 @@ Installer les téchargements réussis? xBRZ x4 xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -971,31 +991,67 @@ Mode exclusif plein écran - le jeu couvrira l"intégralité de votre écra Montrer l'intro - + Active Actif - + Disabled Désactivé - + Enable Activé - + Not Installed Pas Installé - + Install Installer + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + Fichier sélectionné non valide + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + Erreur d'extraction ! + + + + + + Heroes Chronicles + + + File size @@ -1213,113 +1269,101 @@ Heroes® of Might and Magic® III HD n"est actuellement pas pris en charge Installer une version compatible de "In The Wake of Gods", une extension Heroes III créée par des fans - + Heroes III installation found! Installation de Heroes III trouvée! - + Copy data to VCMI folder? Copier le dossier data dans le dossier VCMI ? - + Select %1 file... param is file extension Sélectionner le fichier %1... - + You have to select %1 file! param is file extension Vous avez sélectionné le fichier %1 ! - + GOG file (*.*) Fichier GOG (*.*) - + File selection Sélection de fichier - + File cannot be opened Le fichier ne peut pas être ouvert - + Invalid file selected Fichier sélectionné non valide - + GOG installer Installateur GOG - + GOG data Données GOG - + No Heroes III data! Pas de données Heroes III! - + Selected files do not contain Heroes III data! Les fichiers sélectionnés ne contiennent pas les données de Heroes III ! - - - - + + + + Heroes III data not found! Données Heroes III introuvables ! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. Impossible de détecter des données Heroes III valides dans le répertoire choisi, Veuillez selectionner un dossier ou les données de Heroes III sont présentes. - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - Vous avez fourni le programme d'installation de GOG Galaxy ! Ce fichier ne contient pas le jeu. Veuillez télécharger le programme d'installation de sauvegarde hors ligne du jeu ! + Vous avez fourni le programme d'installation de GOG Galaxy ! Ce fichier ne contient pas le jeu. Veuillez télécharger le programme d'installation de sauvegarde hors ligne du jeu ! - - Stream error while extracting files! -error reason: - 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 ! - - - + Extracting error! Erreur d'extraction ! - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Les fichiers de Heroes III HD Edition ne sont pas supportés par VCMI. Veuillez sélectionner un dossier contenant les données de Heroes III: Complete Edition ou Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Version inconnue ou non supportée de Heroes III. @@ -1334,6 +1378,26 @@ Veuillez sélectionner un dossier contenant les données de Heroes III: Complete Afficheur d'Image + + Innoextract + + + Stream error while extracting files! +error reason: + 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 ! + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language @@ -1485,7 +1549,7 @@ Veuillez sélectionner un dossier contenant les données de Heroes III: Complete Failed to start %1 Reason: %2 Échec de démarrage %1 -Raison : %2 +Raison : %2 diff --git a/launcher/translation/german.ts b/launcher/translation/german.ts index e7d9f1c09..659788841 100644 --- a/launcher/translation/german.ts +++ b/launcher/translation/german.ts @@ -233,7 +233,7 @@ - + Description Beschreibung @@ -293,179 +293,189 @@ Abbrechen - + Mod name Mod-Name - + Installed version Installierte Version - + Latest version Letzte Version - + Size Größe - + Download size Downloadgröße - + Authors Autoren - + License Lizenz - + Contact Kontakt - + Compatibility Kompatibilität - - + + Required VCMI version Benötigte VCMI Version - + Supported VCMI version Unterstützte VCMI Version - + please upgrade mod bitte Mod upgraden - - + + mods repository index Mod Verzeichnis Index - + or newer oder neuer - + Supported VCMI versions Unterstützte VCMI Versionen - + Languages Sprachen - + Required mods Benötigte Mods - + Conflicting mods Mods mit Konflikt - + This mod can not be installed or enabled because the following dependencies are not present Diese Mod kann nicht installiert oder aktiviert werden, da die folgenden Abhängigkeiten nicht vorhanden sind - + This mod can not be enabled because the following mods are incompatible with it Diese Mod kann nicht aktiviert werden, da folgende Mods nicht mit dieser Mod kompatibel sind - + This mod cannot be disabled because it is required by the following mods Diese Mod kann nicht deaktiviert werden, da sie zum Ausführen der folgenden Mods erforderlich ist - + This mod cannot be uninstalled or updated because it is required by the following mods Diese Mod kann nicht deinstalliert oder aktualisiert werden, da sie für die folgenden Mods erforderlich ist - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Dies ist eine Submod und kann nicht separat von der Hauptmod installiert oder deinstalliert werden - + Notes Anmerkungen - + All supported files Alle unterstützten Dateien - + Maps Karten - + Campaigns Kampagnen - + Configs Konfigurationen - + Mods Mods - - Select files (configs, mods, maps, campaigns) to install... - Wähle Dateien (Konfigurationen, Mods, Karten, Kampagnen) zum installieren... + + Gog files + - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? Konfigurationsdatei ersetzen? - + Do you want to replace %1? Soll %1 ersetzt werden? - + Downloading %1. %p% (%v MB out of %m MB) finished Downloade %1. %p% (%v MB von %m MB) abgeschlossen - + Download failed Download fehlgeschlagen - + Unable to download all files. Encountered errors: @@ -478,7 +488,7 @@ Es sind Fehler aufgetreten: - + Install successfully downloaded? @@ -487,34 +497,39 @@ Install successfully downloaded? Installation erfolgreich heruntergeladen? - + + Installing chronicles + + + + Installing mod %1 Installation von Mod %1 - + Operation failed Operation fehlgeschlagen - + Encountered errors: Aufgetretene Fehler: - + screenshots Screenshots - + Screenshot %1 Screenshot %1 - + Mod is incompatible Mod ist inkompatibel @@ -619,7 +634,7 @@ Installation erfolgreich heruntergeladen? CSettingsView - + Off Aus @@ -713,6 +728,11 @@ Installation erfolgreich heruntergeladen? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -879,7 +899,7 @@ Installation erfolgreich heruntergeladen? Renderer - + On An @@ -966,31 +986,67 @@ Exklusiver Vollbildmodus - das Spiel bedeckt den gesamten Bildschirm und verwend Intro anzeigen - + Active Aktiv - + Disabled Deaktiviert - + Enable Aktivieren - + Not Installed Nicht installiert - + Install Installieren + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + Ungültige Datei ausgewählt + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + Fehler beim Extrahieren! + + + + + + Heroes Chronicles + + + File size @@ -1208,113 +1264,101 @@ Der Offline-Installer besteht aus zwei Teilen, .exe und .bin. Stellen Sie sicher In The Wake of Gods - + Heroes III installation found! Heroes III-Installation gefunden! - + Copy data to VCMI folder? Daten in den VCMI-Ordner kopieren? - + Select %1 file... param is file extension %1 Datei auswählen... - + You have to select %1 file! param is file extension Sie müssen %1 Datei auswählen! - + GOG file (*.*) GOG Datei (*.*) - + File selection Dateiauswahl - + File cannot be opened Datei kann nicht geöffnet werden - + Invalid file selected Ungültige Datei ausgewählt - + GOG installer GOG-Installationsprogramm - + GOG data GOG-Datendatei - + No Heroes III data! Keine Heroes III-Daten! - + Selected files do not contain Heroes III data! Die ausgewählten Dateien enthalten keine Heroes III-Daten! - - - - + + + + Heroes III data not found! Heroes III Daten nicht gefunden! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. Es konnten keine gültigen Heroes III-Daten im gewählten Verzeichnis gefunden werden. Bitte wählen Sie ein Verzeichnis mit installierten Heroes III-Daten. - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! Es wurde der GOG Galaxy-Installer ausgewählt! Das Spiel ist in dieser Datei nicht enthalten. Lade den Offline-Backup-Installer für das Spiel herunter! - - Stream error while extracting files! -error reason: - Stream-Fehler beim Extrahieren von Dateien! -Fehlerursache: - - - - Not a supported Inno Setup installer! - Kein unterstütztes Inno Setup Installationsprogramm! - - - + Extracting error! Fehler beim Extrahieren! - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Heroes III: HD Edition Dateien werden von VCMI nicht unterstützt. Bitte wählen Sie ein Verzeichnis mit Heroes III: Complete Edition oder Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Unbekannte oder nicht unterstützte Heroes III-Version gefunden. @@ -1329,6 +1373,26 @@ Bitte wählen Sie ein Verzeichnis mit Heroes III: Complete Edition oder Heroes I Bildbetrachter + + Innoextract + + + Stream error while extracting files! +error reason: + Stream-Fehler beim Extrahieren von Dateien! +Fehlerursache: + + + + Not a supported Inno Setup installer! + Kein unterstütztes Inno Setup Installationsprogramm! + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/polish.ts b/launcher/translation/polish.ts index 46b52f1cd..c30a428c1 100644 --- a/launcher/translation/polish.ts +++ b/launcher/translation/polish.ts @@ -233,7 +233,7 @@ - + Description Opis @@ -293,179 +293,189 @@ Przerwij - + Mod name Nazwa moda - + Installed version Zainstalowana wersja - + Latest version Najnowsza wersja - + Size Rozmiar - + Download size Rozmiar pobierania - + Authors Autorzy - + License Licencja - + Contact Kontakt - + Compatibility Kompatybilność - - + + Required VCMI version Wymagana wersja VCMI - + Supported VCMI version Wspierana wersja VCMI - + please upgrade mod proszę zaktualizować moda - - + + mods repository index indeks repozytorium modów - + or newer lub nowsze - + Supported VCMI versions Wspierane wersje VCMI - + Languages Języki - + Required mods Wymagane mody - + Conflicting mods Konfliktujące mody - + This mod can not be installed or enabled because the following dependencies are not present Ten mod nie może zostać zainstalowany lub włączony ponieważ następujące zależności nie zostały spełnione - + This mod can not be enabled because the following mods are incompatible with it Ten mod nie może zostać włączony ponieważ następujące mody są z nim niekompatybilne - + This mod cannot be disabled because it is required by the following mods Ten mod nie może zostać wyłączony ponieważ jest wymagany do uruchomienia następujących modów - + This mod cannot be uninstalled or updated because it is required by the following mods Ten mod nie może zostać odinstalowany lub zaktualizowany ponieważ jest wymagany do uruchomienia następujących modów - + This is a submod and it cannot be installed or uninstalled separately from its parent mod To jest moduł składowy innego moda i nie może być zainstalowany lub odinstalowany oddzielnie od moda nadrzędnego - + Notes Uwagi - + All supported files Wszystkie wspierane pliki - + Maps Mapy - + Campaigns Kampanie - + Configs Konfiguracje - + Mods Mody - - Select files (configs, mods, maps, campaigns) to install... - Wybierz pliki (konfiguracyjne, mody, mapy, kampanie) do zainstalowania... + + Gog files + - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? Zastąpić plik konfiguracji? - + Do you want to replace %1? Czy chcesz zastąpić %1? - + Downloading %1. %p% (%v MB out of %m MB) finished Pobieranie %1. %p% (%v MB z %m MB) ukończono - + Download failed Pobieranie nieudane - + Unable to download all files. Encountered errors: @@ -478,7 +488,7 @@ Napotkane błędy: - + Install successfully downloaded? @@ -487,34 +497,39 @@ Install successfully downloaded? Zainstalować pomyślnie pobrane? - + + Installing chronicles + + + + Installing mod %1 Instalowanie modyfikacji %1 - + Operation failed Operacja nieudana - + Encountered errors: Napotkane błędy: - + screenshots zrzuty ekranu - + Screenshot %1 Zrzut ekranu %1 - + Mod is incompatible Mod jest niekompatybilny @@ -619,7 +634,7 @@ Zainstalować pomyślnie pobrane? CSettingsView - + Off Wyłączony @@ -713,6 +728,11 @@ Zainstalować pomyślnie pobrane? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -879,7 +899,7 @@ Zainstalować pomyślnie pobrane? Renderer - + On Włączony @@ -966,31 +986,67 @@ Pełny ekran klasyczny - gra przysłoni cały ekran uruchamiając się w wybrane Pokaż intro - + Active Aktywny - + Disabled Wyłączone - + Enable Włącz - + Not Installed Nie zainstalowano - + Install Zainstaluj + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + Wybrano nieprawidłowy plik + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + Błąd wypakowywania! + + + + + + Heroes Chronicles + + + File size @@ -1208,113 +1264,101 @@ Instalator offline składa się z dwóch części, .exe i .bin. Upewnij się, ż In The Wake of Gods - + Heroes III installation found! Znaleziono zainstalowane Heroes III! - + Copy data to VCMI folder? Skopiować dane do folderu VCMI? - + Select %1 file... param is file extension Wybierz plik %1... - + You have to select %1 file! param is file extension Musisz wybrać plik %1! - + GOG file (*.*) Instalator GOG (*.*) - + File selection Wybór pliku - + File cannot be opened Nieudane otwarcie pliku - + Invalid file selected Wybrano nieprawidłowy plik - + GOG installer Instalator GOG - + GOG data Dane GOG - + No Heroes III data! Brak danych Heroes III! - + Selected files do not contain Heroes III data! Wybrane pliki nie zawierają danych Heroes III! - - - - + + + + Heroes III data not found! Dane Heroes III nie znalezione! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. Nieudane znalezienie poprawnych plików Heroes III w podanej lokalizacji. Proszę wybrać folder z zainstalowanymi danymi Heroes III. - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! Podany plik jest instalatorem GOG Galaxy! Ten plik nie zawiera gry. Proszę pobrać zapasowy instalator offline gry! - - Stream error while extracting files! -error reason: - Błąd strumienia podczas rozpakowywania plików! -powód błędu: - - - - Not a supported Inno Setup installer! - To nie jest wspierany instalator Inno Setup! - - - + Extracting error! Błąd wypakowywania! - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Pliki Heroes III HD Edition nie są wspierane przez VCMI. Proszę wybrać folder z Heroes III: Complete Edition lub Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Znaleziono nieznaną lub niewspieraną wersję Heroes III. @@ -1329,6 +1373,26 @@ Proszę wybrać folder z Heroes III: Complete Edition lub Heroes III: Shadow of Wyświetlacz obrazków + + Innoextract + + + Stream error while extracting files! +error reason: + Błąd strumienia podczas rozpakowywania plików! +powód błędu: + + + + Not a supported Inno Setup installer! + To nie jest wspierany instalator Inno Setup! + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/portuguese.ts b/launcher/translation/portuguese.ts index 66f100f81..dfe5d75c8 100644 --- a/launcher/translation/portuguese.ts +++ b/launcher/translation/portuguese.ts @@ -233,7 +233,7 @@ - + Description Descrição @@ -293,179 +293,189 @@ Cancelar - + Mod name Nome do mod - + Installed version Versão instalada - + Latest version Última versão - + Size Tamanho - + Download size Tamanho do download - + Authors Autores - + License Licença - + Contact Contato - + Compatibility Compatibilidade - - + + Required VCMI version Versão do VCMI necessária - + Supported VCMI version Versão do VCMI suportada - + please upgrade mod por favor, atualize o mod - - + + mods repository index índice do repositório de mods - + or newer ou mais recente - + Supported VCMI versions Versões do VCMI suportadas - + Languages Idiomas - + Required mods Mods requeridos - + Conflicting mods Mods conflitantes - + This mod can not be installed or enabled because the following dependencies are not present Este mod não pode ser instalado ou ativado porque as seguintes dependências não estão presentes - + This mod can not be enabled because the following mods are incompatible with it Este mod não pode ser ativado porque os seguintes mods são incompatíveis com ele - + This mod cannot be disabled because it is required by the following mods Este mod não pode ser desativado porque é necessário pelos seguintes mods - + This mod cannot be uninstalled or updated because it is required by the following mods Este mod não pode ser desinstalado ou atualizado porque é necessário pelos seguintes mods - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Este é um submod e não pode ser instalado ou desinstalado separadamente do seu mod principal - + Notes Notas - + All supported files Todos os arquivos suportados - + Maps Mapas - + Campaigns Campanhas - + Configs Configurações - + Mods Mods - - Select files (configs, mods, maps, campaigns) to install... - Selecione arquivos (configurações, mods, mapas, campanhas) para instalar... + + Gog files + - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? Substituir arquivo de configuração? - + Do you want to replace %1? Você deseja substituir %1? - + Downloading %1. %p% (%v MB out of %m MB) finished Baixando %1. %p% (%v MB de %m MB) concluído - + Download failed Falha no download - + Unable to download all files. Encountered errors: @@ -478,7 +488,7 @@ Encontrados os seguintes erros: - + Install successfully downloaded? @@ -487,34 +497,39 @@ Install successfully downloaded? Instalar o download realizado com sucesso? - + + Installing chronicles + + + + Installing mod %1 Instalando mod %1 - + Operation failed Falha na operação - + Encountered errors: Erros encontrados: - + screenshots capturas de tela - + Screenshot %1 Captura de tela %1 - + Mod is incompatible O mod é incompatível @@ -619,7 +634,7 @@ Instalar o download realizado com sucesso? CSettingsView - + Off Desativado @@ -841,7 +856,7 @@ Instalar o download realizado com sucesso? Downscaling Filter - Filtro de Redução de Escala + Filtro de Redução de Escala @@ -884,12 +899,12 @@ Instalar o download realizado com sucesso? Renderizador - + On Ativado - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -971,31 +986,67 @@ Modo de tela cheia exclusivo - o jogo cobrirá toda a sua tela e usará a resolu Mostrar introdução - + Active Ativo - + Disabled Desativado - + Enable Ativar - + Not Installed Não Instalado - + Install Instalar + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + Arquivo selecionado inválido + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + Erro ao extrair! + + + + + + Heroes Chronicles + + + File size @@ -1178,7 +1229,7 @@ O instalador offline consiste em duas partes, .exe e .bin. Certifique-se de baix - Heroes III data files + Heroes III data files Arquivos de dados do Heroes III @@ -1213,113 +1264,101 @@ O instalador offline consiste em duas partes, .exe e .bin. Certifique-se de baix In The Wake of Gods - + Heroes III installation found! Instalação do Heroes III encontrada! - + Copy data to VCMI folder? Copiar dados para a pasta do VCMI? - + Select %1 file... param is file extension Selecionar arquivo %1... - + You have to select %1 file! param is file extension Você precisa selecionar o arquivo %1! - + GOG file (*.*) Arquivo GOG (*.*) - + File selection Seleção de arquivo - + File cannot be opened O arquivo não pode ser aberto - + Invalid file selected Arquivo selecionado inválido - + GOG installer Instalador GOG - + GOG data Dados do GOG - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! Você forneceu o instalador do GOG Galaxy! Este arquivo não contém o jogo. Por favor, faça o download do instalador offline de backup do jogo! - - Stream error while extracting files! -error reason: - Erro de fluxo ao extrair arquivos! -Motivo do erro: - - - - Not a supported Inno Setup installer! - Instalador Inno Setup não suportado! - - - + Extracting error! Erro ao extrair! - + No Heroes III data! Nenhum dado do Heroes III! - + Selected files do not contain Heroes III data! Os arquivos selecionados não contêm dados do Heroes III! - - - - + + + + Heroes III data not found! Dados do Heroes III não encontrados! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. Falha ao detectar dados válidos do Heroes III no diretório escolhido. Por favor, selecione o diretório com os dados do Heroes III instalados. - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Arquivos do Heroes III: HD Edition não são suportados pelo VCMI. Por favor, selecione o diretório com Heroes III: Complete Edition ou Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Versão desconhecida ou não suportada do Heroes III encontrada. @@ -1334,6 +1373,26 @@ Por favor, selecione o diretório com Heroes III: Complete Edition ou Heroes III Visualizador de Imagens + + Innoextract + + + Stream error while extracting files! +error reason: + Erro de fluxo ao extrair arquivos! +Motivo do erro: + + + + Not a supported Inno Setup installer! + Instalador Inno Setup não suportado! + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/russian.ts b/launcher/translation/russian.ts index 6578bc373..b63ae0386 100644 --- a/launcher/translation/russian.ts +++ b/launcher/translation/russian.ts @@ -233,7 +233,7 @@ - + Description Описание @@ -293,179 +293,189 @@ Отмена - + Mod name Название мода - + Installed version Установленная версия - + Latest version Последняя версия - + Size - + Download size Размер загрузки - + Authors Авторы - + License Лицензия - + Contact Контакты - + Compatibility Совместимость - - + + Required VCMI version Требуемая версия VCMI - + Supported VCMI version Поддерживаемая версия VCMI - + please upgrade mod - - + + mods repository index - + or newer - + Supported VCMI versions Поддерживаемые версии VCMI - + Languages Языки - + Required mods Зависимости - + Conflicting mods Конфликтующие моды - + This mod can not be installed or enabled because the following dependencies are not present Этот мод не может быть установлен или активирован, так как отсутствуют следующие зависимости - + This mod can not be enabled because the following mods are incompatible with it Этот мод не может быть установлен или активирован, так как следующие моды несовместимы с этим - + This mod cannot be disabled because it is required by the following mods Этот мод не может быть выключен, так как он является зависимостью для следующих - + This mod cannot be uninstalled or updated because it is required by the following mods Этот мод не может быть удален или обновлен, так как является зависимостью для следующих модов - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Это вложенный мод, он не может быть установлен или удален отдельно от родительского - + Notes Замечания - + All supported files - + Maps - + Campaigns - + Configs - + Mods Моды - - Select files (configs, mods, maps, campaigns) to install... + + Gog files - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? - + Do you want to replace %1? - + Downloading %1. %p% (%v MB out of %m MB) finished - + Download failed - + Unable to download all files. Encountered errors: @@ -474,40 +484,45 @@ Encountered errors: - + Install successfully downloaded? - + + Installing chronicles + + + + Installing mod %1 - + Operation failed - + Encountered errors: - + screenshots - + Screenshot %1 Скриншот %1 - + Mod is incompatible Мод несовместим @@ -616,12 +631,12 @@ Install successfully downloaded? - + Off Отключено - + On Включено @@ -735,6 +750,11 @@ Install successfully downloaded? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -952,31 +972,67 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use Вступление - + Active Активен - + Disabled Отключен - + Enable Включить - + Not Installed Не установлен - + Install Установить + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + + + + + + + Heroes Chronicles + + + File size @@ -1193,110 +1249,99 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b - + Heroes III installation found! - + Copy data to VCMI folder? - + Select %1 file... param is file extension - + You have to select %1 file! param is file extension - + GOG file (*.*) - + File selection - + File cannot be opened - + Invalid file selected - + GOG installer - + GOG data - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - - Stream error while extracting files! -error reason: - - - - - Not a supported Inno Setup installer! - - - - + Extracting error! - + No Heroes III data! - + Selected files do not contain Heroes III data! - - - - + + + + Heroes III data not found! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. @@ -1310,6 +1355,25 @@ Please select directory with Heroes III: Complete Edition or Heroes III: Shadow Просмотр изображений + + Innoextract + + + Stream error while extracting files! +error reason: + + + + + Not a supported Inno Setup installer! + + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/spanish.ts b/launcher/translation/spanish.ts index 0c7f269a5..0b853743b 100644 --- a/launcher/translation/spanish.ts +++ b/launcher/translation/spanish.ts @@ -233,7 +233,7 @@ - + Description Descripción @@ -293,179 +293,189 @@ Cancelar - + Mod name Nombre del mod - + Installed version Versión instalada - + Latest version Última versión - + Size Tamaño - + Download size Tamaño de descarga - + Authors Autores - + License Licencia - + Contact Contacto - + Compatibility Compatibilidad - - + + Required VCMI version Versión de VCMI requerida - + Supported VCMI version Versión de VCMI compatible - + please upgrade mod - - + + mods repository index - + or newer - + Supported VCMI versions Versiones de VCMI compatibles - + Languages Idiomas - + Required mods Mods requeridos - + Conflicting mods Mods conflictivos - + This mod can not be installed or enabled because the following dependencies are not present Este mod no se puede instalar o habilitar porque no están presentes las siguientes dependencias - + This mod can not be enabled because the following mods are incompatible with it Este mod no se puede habilitar porque los siguientes mods son incompatibles con él - + This mod cannot be disabled because it is required by the following mods No se puede desactivar este mod porque es necesario para ejecutar los siguientes mods - + This mod cannot be uninstalled or updated because it is required by the following mods No se puede desinstalar o actualizar este mod porque es necesario para ejecutar los siguientes mods - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Este es un submod y no se puede instalar o desinstalar por separado del mod principal - + Notes Notas - + All supported files - + Maps Mapas - + Campaigns - + Configs - + Mods Mods - - Select files (configs, mods, maps, campaigns) to install... + + Gog files - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? - + Do you want to replace %1? - + Downloading %1. %p% (%v MB out of %m MB) finished - + Download failed Descarga fallida - + Unable to download all files. Encountered errors: @@ -478,7 +488,7 @@ Errores encontrados: - + Install successfully downloaded? @@ -487,34 +497,39 @@ Install successfully downloaded? Instalar lo correctamente descargado? - + + Installing chronicles + + + + Installing mod %1 Instalando mod %1 - + Operation failed Operación fallida - + Encountered errors: Errores encontrados: - + screenshots - + Screenshot %1 Captura de pantalla %1 - + Mod is incompatible El mod es incompatible @@ -618,7 +633,7 @@ Instalar lo correctamente descargado? CSettingsView - + Off Desactivado @@ -712,6 +727,11 @@ Instalar lo correctamente descargado? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -878,7 +898,7 @@ Instalar lo correctamente descargado? Render - + On Activado @@ -965,31 +985,67 @@ Pantalla completa - el juego cubrirá la totalidad de la pantalla y utilizará l Comprovar al inicio - + Active Activado - + Disabled Desactivado - + Enable Activar - + Not Installed No Instalado - + Install Instalar + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + + + + + + + Heroes Chronicles + + + File size @@ -1206,110 +1262,99 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b Finalizar - + Heroes III installation found! Instalación de Heroes III encontrada! - + Copy data to VCMI folder? Copiar datos a la carpeta VCMI? - + Select %1 file... param is file extension - + You have to select %1 file! param is file extension - + GOG file (*.*) - + File selection - + File cannot be opened - + Invalid file selected - + GOG installer - + GOG data - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - - Stream error while extracting files! -error reason: - - - - - Not a supported Inno Setup installer! - - - - + Extracting error! - + No Heroes III data! - + Selected files do not contain Heroes III data! - - - - + + + + Heroes III data not found! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. @@ -1323,6 +1368,25 @@ Please select directory with Heroes III: Complete Edition or Heroes III: Shadow Visor de imágenes + + Innoextract + + + Stream error while extracting files! +error reason: + + + + + Not a supported Inno Setup installer! + + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/swedish.ts b/launcher/translation/swedish.ts index 575b22836..deb1fd13b 100644 --- a/launcher/translation/swedish.ts +++ b/launcher/translation/swedish.ts @@ -102,7 +102,7 @@ Test - Testa + Test @@ -224,7 +224,7 @@ Active - Tillgångar + Aktiv @@ -238,7 +238,7 @@ - + Description Beskrivning @@ -290,182 +290,192 @@ Abort - Ge upp + Avbryt - + Mod name Modd-namn - + Installed version Installerad version - + Latest version Senaste version - + Size Storlek - + Download size - Nedladdningsstorlek + Nedladdnings-storlek - + Authors Författare - + License Licens - + Contact Kontakt - + Compatibility Kompatibilitet - - + + Required VCMI version VCMI-version som krävs - + Supported VCMI version - Stödd VCMI-version + VCMI-version som stöds - + please upgrade mod vänligen uppdatera modd - - + + mods repository index - Modd-repositorie index + Modd-repositorie-index - + or newer eller nyare - + Supported VCMI versions VCMI-versioner som stöds - + Languages Språk - + Required mods Moddar som krävs - + Conflicting mods Modd-konflikter - + This mod can not be installed or enabled because the following dependencies are not present Denna modd kan inte installeras eller aktiveras eftersom följande beroenden inte finns - + This mod can not be enabled because the following mods are incompatible with it - Denna modd kan inte aktiveras eftersom följande beroenden är inkompatibla med den + Denna modd kan inte aktiveras eftersom följande moddar är inkompatibla med den - + This mod cannot be disabled because it is required by the following mods - Denna modd kan inte inaktiveras eftersom den krävs för följande beroenden + Denna modd kan inte inaktiveras eftersom den krävs för följande modd - + This mod cannot be uninstalled or updated because it is required by the following mods - Denna modd kan inte avinstalleras eller uppdateras eftersom den krävs för följande beroenden + Denna modd kan inte avinstalleras eller uppdateras eftersom den krävs för följande modd - + This is a submod and it cannot be installed or uninstalled separately from its parent mod - Denna undermodd/submodd kan inte installeras eller uppdateras separat från huvud-modden + Detta är en undermodd/submodd och den kan inte installeras eller avinstalleras separat från huvud-modden - + Notes Anteckningar - + All supported files Alla filer som stöds - + Maps Kartor - + Campaigns Kampanjer - + Configs Konfigurationer - + Mods Moddar - - Select files (configs, mods, maps, campaigns) to install... - Välj filer att installera (konfigurationer, moddar, kartor, kampanjer)... + + Gog files + - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? Byt ut konfigurationsfilen? - + Do you want to replace %1? Vill du ersätta %1? - + Downloading %1. %p% (%v MB out of %m MB) finished - Ladda ner %1. %p% (%v MB av %m MB) slutfört + Laddar ner %1. %p% (%v MB av %m MB) slutfört - + Download failed Nedladdning misslyckades - + Unable to download all files. Encountered errors: @@ -478,43 +488,48 @@ Fel påträffat: - + Install successfully downloaded? -Installera lyckade nedladdningar? +Installation framgångsrikt nedladdad? - + + Installing chronicles + + + + Installing mod %1 - Installera mod %1 + Installera modd %1 - + Operation failed Åtgärden misslyckades - + Encountered errors: Fel påträffades: - + screenshots skärmdumpar - + Screenshot %1 - Skriv ut skärm %1 + Skärmbild %1 - + Mod is incompatible Denna modd är inkompatibel @@ -555,7 +570,7 @@ Installera lyckade nedladdningar? Mod is not compatible, please update VCMI and checkout latest mod revisions - Modden är inte kompatibel. Vänligen uppdatera VCMI och kontrollera att du har den senaste versionen av modden + Modden är inte kompatibel. Vänligen uppdatera VCMI och kontrollera att du har den senaste kompatibla versionen av modden @@ -565,7 +580,7 @@ Installera lyckade nedladdningar? Required mod %1 is not enabled - Den obligatorisk modden %1 är inte aktiverad + Den obligatoriska modden %1 är inte aktiverad @@ -601,7 +616,7 @@ Installera lyckade nedladdningar? Failed to extract mod data - Det gick inte att extrahera data från modd + Misslyckades att extrahera data från modd @@ -619,7 +634,7 @@ Installera lyckade nedladdningar? CSettingsView - + Off Inaktiverad @@ -629,7 +644,7 @@ Installera lyckade nedladdningar? Artificiell intelligens - + On Aktiverad @@ -646,7 +661,7 @@ Installera lyckade nedladdningar? VSync - Vertikal synkronisering + Vertikal-synkronisering (VSync) @@ -676,7 +691,7 @@ Installera lyckade nedladdningar? Software Cursor - Programvarumarkör (muspekare) + Programvarustyrd muspekare @@ -703,6 +718,11 @@ Installera lyckade nedladdningar? xBRZ x4 xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -711,7 +731,7 @@ Installera lyckade nedladdningar? Upscaling Filter - Förstoringsfilter + Uppskalnings-filter @@ -746,7 +766,7 @@ Installera lyckade nedladdningar? Show Tutorial again - Visa övningsgenomgången igen + Visa handledningen/övningsgenomgången igen @@ -833,7 +853,7 @@ Exklusivt helskärmsläge - spelet kommer att täcka hela skärmen och använda Borderless fullscreen - Kantlöst fönsterläge + Kantlös helskärm @@ -903,7 +923,7 @@ Exklusivt helskärmsläge - spelet kommer att täcka hela skärmen och använda Check on startup - Kontrollera vid start + Kontrollera vid uppstart @@ -966,31 +986,67 @@ Exklusivt helskärmsläge - spelet kommer att täcka hela skärmen och använda Visa intro - + Active Aktiv - + Disabled Inaktiverad - + Enable Aktivera - + Not Installed Inte installerad - + Install Installera + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + Ogiltig fil vald + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + Extraktionsfel! + + + + + + Heroes Chronicles + + + File size @@ -1059,7 +1115,7 @@ Exklusivt helskärmsläge - spelet kommer att täcka hela skärmen och använda You can manually copy directories Maps, Data and Mp3 from the original game directory to VCMI data directory that you can see on top of this page - Du kan manuellt kopiera mapparna 'Maps', 'Data' och 'Mp3' från den ursprungliga spel-mappen till VCMI-datamappen som du kan se överst på den här sidan + Du kan manuellt kopiera mapparna 'Maps', 'Data' och 'Mp3' från den ursprungliga spel-mappen till VCMI-datamappen som du kan se överst på den här sidan @@ -1095,12 +1151,12 @@ Exklusivt helskärmsläge - spelet kommer att täcka hela skärmen och använda If you own Heroes III on gog.com you can download backup offline installer from gog.com, and VCMI will import Heroes III data using offline installer. Offline installer consists of two parts, .exe and .bin. Make sure you download both of them. - Om du äger Heroes III från GOG.com kan du ladda ner backup offline-installationsprogrammet från GOG.com - VCMI kommer att importera Heroes III-data med hjälp av offline-installationsprogrammet. Offline-installationsprogrammet består av två delar, en .exe och en .bin. Se till att ladda ner båda. + Om du äger Heroes III från GOG.com kan du ladda ner backup offline-installationsprogrammet från 'GOG.com'. VCMI kommer att importera Heroes III-data med hjälp av offline-installationsprogrammet. Offline-installationsprogrammet består av två delar, en '.exe'- och en '.bin'fil. Se till att ladda ner båda. Install a translation of Heroes III in your preferred language - Installera en översättning av Heroes III på ditt föredragna språk + Installera en översättning av Heroes III på det språk du föredrar @@ -1110,12 +1166,12 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b VCMI on Github - VCMI på Github + VCMI på 'Github' VCMI on Discord - VCMI på Discord + VCMI på 'Discord' @@ -1189,7 +1245,7 @@ Heroes® of Might and Magic® III HD stöds för närvarande inte! Optionally, you can install additional mods either now, or at any point later, using the VCMI Launcher - Valfritt kan du installera ytterligare moddar antingen nu eller när som helst senare med hjälp av 'VCMI Launcher' + Du kan välja att installera ytterligare moddar, antingen nu eller vid ett senare tillfälle med hjälp av 'VCMI Launchern' @@ -1207,112 +1263,100 @@ Heroes® of Might and Magic® III HD stöds för närvarande inte! Installera en kompatibel version av "In The Wake of Gods" (en fantillverkad Heroes III-expansion) - + Heroes III installation found! Heroes III-installationen hittades! - + Copy data to VCMI folder? Kopiera data till VCMI-mappen? - + Select %1 file... param is file extension - Välj filen %1... + Välj filen %1 ... - + You have to select %1 file! param is file extension Du behöver välja filen %1! - + GOG file (*.*) GOG-fil (*.*) - + File selection Filval - + File cannot be opened Filen kan inte öppnas - + Invalid file selected Ogiltig fil vald - + GOG installer - GOG installationsprogram + GOG-Installationsprogram - + GOG data GOG-data - + No Heroes III data! Inga Heroes III-data! - + Selected files do not contain Heroes III data! De valda filerna innehåller inte Heroes III-data! - - - - + + + + Heroes III data not found! Heroes III-data hittades inte! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. - Det går inte att upptäcka giltiga Heroes III-data i den valda katalogen. Välj en mapp där Heroes III-data finns. + Misslyckades med att upptäcka giltiga Heroes III-data i den valda mappen. Vänligen välj en mapp där Heroes III-data finns installerat. - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! Du har tillhandahållit installationsprogrammet för GOG Galaxy! Den här filen innehåller inte spelet. Ladda ner offline-installationsprogrammet för spelet! - - Stream error while extracting files! -error reason: - Strömningsfel vid extrahering av filer! -Orsak till fel: - - - - Not a supported Inno Setup installer! - Inno Setup-installationsprogrammet stöds inte! - - - + Extracting error! - Extraktionsfel! + Fel vid extrahering! - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Heroes III HD Edition-filer stöds inte av VCMI. -Välj en mapp som innehåller data från Heroes III: Complete Edition eller Heroes III: Shadow of Death. +Vänligen välj en mapp som innehåller data från Heroes III: Complete Edition eller Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Okänd eller ostödd version av Heroes III. @@ -1327,6 +1371,26 @@ Vänligen välj en mapp som innehåller data från Heroes III: Complete Edition Bildvisare + + Innoextract + + + Stream error while extracting files! +error reason: + Strömningsfel vid extrahering av filer! +Orsak till fel: + + + + Not a supported Inno Setup installer! + Inno Setup-installationsprogrammet stöds inte! + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language @@ -1425,7 +1489,7 @@ Vänligen välj en mapp som innehåller data från Heroes III: Complete Edition VCMI Launcher - VCMI Launcher + VCMI-startprogram (VCMI Launcher) @@ -1450,7 +1514,7 @@ Vänligen välj en mapp som innehåller data från Heroes III: Complete Edition Start game - Starta ett spel + Starta spelet diff --git a/launcher/translation/ukrainian.ts b/launcher/translation/ukrainian.ts index 41daca511..ddfe4a510 100644 --- a/launcher/translation/ukrainian.ts +++ b/launcher/translation/ukrainian.ts @@ -233,7 +233,7 @@ - + Description Опис @@ -293,179 +293,189 @@ Відмінити - + Mod name Назва модифікації - + Installed version Встановлена версія - + Latest version Найновіша версія - + Size Розмір - + Download size Розмір для завантаження - + Authors Автори - + License Ліцензія - + Contact Контакти - + Compatibility Сумісність - - + + Required VCMI version Необхідна версія VCMI - + Supported VCMI version Підтримувана версія VCMI - + please upgrade mod будь ласка, оновіть модифікацію - - + + mods repository index каталог модифікацій - + or newer або новіше - + Supported VCMI versions Підтримувані версії VCMI - + Languages Мови - + Required mods Необхідні модифікації - + Conflicting mods Конфліктуючі модифікації - + This mod can not be installed or enabled because the following dependencies are not present Цю модифікацію не можна встановити чи активувати, оскільки відсутні наступні залежності - + This mod can not be enabled because the following mods are incompatible with it Цю модифікацію не можна ввімкнути, оскільки наступні модифікації несумісні з цією модифікацією - + This mod cannot be disabled because it is required by the following mods Цю модифікацію не можна відключити, оскільки вона необхідна для запуску наступних модифікацій - + This mod cannot be uninstalled or updated because it is required by the following mods Цю модифікацію не можна видалити або оновити, оскільки вона необхідна для запуску наступних модифікацій - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Це вкладена модифікація, і її не можна встановити або видалити окремо від батьківської модифікації - + Notes Примітки - + All supported files Усі підтримувані файли - + Maps Мапи - + Campaigns Кампанії - + Configs Налаштування - + Mods Модифікації - - Select files (configs, mods, maps, campaigns) to install... - Виберіть файли ( налаштування, моди, мапи, кампанії) для встановлення... + + Gog files + - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? Замінити файл налаштувань? - + Do you want to replace %1? Ви дійсно хочете замінити %1? - + Downloading %1. %p% (%v MB out of %m MB) finished Завантажується %1. %p% (%v MB з %m MB) завершено - + Download failed Помилка завантаження - + Unable to download all files. Encountered errors: @@ -478,7 +488,7 @@ Encountered errors: - + Install successfully downloaded? @@ -487,34 +497,39 @@ Install successfully downloaded? Встановити успішно завантажені? - + + Installing chronicles + + + + Installing mod %1 Встановлення модифікації %1 - + Operation failed Операція завершилася невдало - + Encountered errors: Виникли помилки: - + screenshots знімки екрану - + Screenshot %1 Знімок екрану %1 - + Mod is incompatible Модифікація несумісна @@ -619,7 +634,7 @@ Install successfully downloaded? CSettingsView - + Off Ні @@ -713,6 +728,11 @@ Install successfully downloaded? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -879,7 +899,7 @@ Install successfully downloaded? Рендерер - + On Так @@ -966,31 +986,67 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use Вступні відео - + Active Активні - + Disabled Деактивований - + Enable Активувати - + Not Installed Не встановлено - + Install Встановити + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + Обрано невірний файл + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + Помилка видобування! + + + + + + Heroes Chronicles + + + File size @@ -1208,113 +1264,101 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b In The Wake of Gods - + Heroes III installation found! Інсталяцію Heroes III знайдено! - + Copy data to VCMI folder? Скопіювати дані до теки VCMI? - + Select %1 file... param is file extension Оберіть файл %1... - + You have to select %1 file! param is file extension Ви повинні обрати файл %1! - + GOG file (*.*) Файл GOG (*.*) - + File selection Вибір файлу - + File cannot be opened Не вдається відкрити файл - + Invalid file selected Обрано невірний файл - + GOG installer Інсталятор GOG - + GOG data Дані GOG - + No Heroes III data! Немає файлів даних Heroes III! - + Selected files do not contain Heroes III data! Обрані файли не містять файлів з грою Heroes III! - - - - + + + + Heroes III data not found! Файли даних Heroes III не знайдено! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. Не вдалося виявити файли Heroes III у вибраному каталозі. Будь ласка, виберіть теку зі встановленими даними Heroes III. - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! Ви надали інсталятор GOG Galaxy! Цей файл не містить гри. Будь ласка, завантажте резервну копію інсталятора гри! - - Stream error while extracting files! -error reason: - Помилка потоку під час розпакування файлів! -причина помилки: - - - - Not a supported Inno Setup installer! - Не підтримуваний інсталятор Inno Setup! - - - + Extracting error! Помилка видобування! - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Файли Heroes III: HD Edition не підтримуються VCMI. Будь ласка, виберіть теку з Heroes III: Complete Edition або Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. Знайдено невідому або не підтримувану версію Heroes III. @@ -1329,6 +1373,26 @@ Please select directory with Heroes III: Complete Edition or Heroes III: Shadow Перегляд зображень + + Innoextract + + + Stream error while extracting files! +error reason: + Помилка потоку під час розпакування файлів! +причина помилки: + + + + Not a supported Inno Setup installer! + Не підтримуваний інсталятор Inno Setup! + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/launcher/translation/vietnamese.ts b/launcher/translation/vietnamese.ts index 24c9e05d1..c5263d4e9 100644 --- a/launcher/translation/vietnamese.ts +++ b/launcher/translation/vietnamese.ts @@ -233,7 +233,7 @@ - + Description Mô tả @@ -293,179 +293,189 @@ Hủy - + Mod name Tên bản sửa đổi - + Installed version Phiên bản cài đặt - + Latest version Phiên bản mới nhất - + Size - + Download size Kích thước tải về - + Authors Tác giả - + License Giấy phép - + Contact Liên hệ - + Compatibility Tương thích - - + + Required VCMI version Cần phiên bản VCMI - + Supported VCMI version Hỗ trợ phiên bản VCMI - + please upgrade mod - - + + mods repository index - + or newer - + Supported VCMI versions Phiên bản VCMI hỗ trợ - + Languages Ngôn ngữ - + Required mods Cần các bản sửa đổi - + Conflicting mods Bản sửa đổi không tương thích - + This mod can not be installed or enabled because the following dependencies are not present Bản sửa đổi này không thể cài đặt hoặc kích hoạt do thiếu các bản sửa đổi sau - + This mod can not be enabled because the following mods are incompatible with it Bản sửa đổi này không thể kích hoạt do không tương thích các bản sửa đổi sau - + This mod cannot be disabled because it is required by the following mods Bản sửa đổi này không thể tắt do cần thiết cho các bản sửa đổi sau - + This mod cannot be uninstalled or updated because it is required by the following mods Bản sửa đổi này không thể gỡ bỏ hoặc nâng cấp do cần thiết cho các bản sửa đổi sau - + This is a submod and it cannot be installed or uninstalled separately from its parent mod Đây là bản con, không thể cài đặt hoặc gỡ bỏ tách biệt với bản cha - + Notes Ghi chú - + All supported files - + Maps - + Campaigns - + Configs - + Mods Bản sửa đổi - - Select files (configs, mods, maps, campaigns) to install... + + Gog files - + + All files (*.*) + + + + + Select files (configs, mods, maps, campaigns, gog files) to install... + + + + Replace config file? - + Do you want to replace %1? - + Downloading %1. %p% (%v MB out of %m MB) finished - + Download failed - + Unable to download all files. Encountered errors: @@ -474,40 +484,45 @@ Encountered errors: - + Install successfully downloaded? - + + Installing chronicles + + + + Installing mod %1 - + Operation failed - + Encountered errors: - + screenshots - + Screenshot %1 Hình ảnh %1 - + Mod is incompatible Bản sửa đổi này không tương thích @@ -611,7 +626,7 @@ Install successfully downloaded? CSettingsView - + Off Tắt @@ -705,6 +720,11 @@ Install successfully downloaded? xBRZ x4 + + + Use scalable fonts + + Online Lobby address @@ -871,7 +891,7 @@ Install successfully downloaded? - + On Bật @@ -958,31 +978,67 @@ Toàn màn hình riêng biệt - Trò chơi chạy toàn màn hình và dùng đ Hiện thị giới thiệu - + Active Bật - + Disabled Tắt - + Enable Bật - + Not Installed Chưa cài đặt - + Install Cài đặt + + ChroniclesExtractor + + + File cannot opened + + + + + + Invalid file selected + + + + + You have to select an gog installer file! + + + + + You have to select an chronicle installer file! + + + + + Extracting error! + + + + + + + Heroes Chronicles + + + File size @@ -1199,110 +1255,99 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b In The Wake of Gods - + Heroes III installation found! - + Copy data to VCMI folder? - + Select %1 file... param is file extension - + You have to select %1 file! param is file extension - + GOG file (*.*) - + File selection - + File cannot be opened - + Invalid file selected - + GOG installer - + GOG data - + You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - - Stream error while extracting files! -error reason: - - - - - Not a supported Inno Setup installer! - - - - + Extracting error! - + No Heroes III data! - + Selected files do not contain Heroes III data! - - - - + + + + Heroes III data not found! - + Failed to detect valid Heroes III data in chosen directory. Please select directory with installed Heroes III data. - + Heroes III: HD Edition files are not supported by VCMI. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. - + Unknown or unsupported Heroes III version found. Please select directory with Heroes III: Complete Edition or Heroes III: Shadow of Death. @@ -1316,6 +1361,25 @@ Please select directory with Heroes III: Complete Edition or Heroes III: Shadow Trình xem ảnh + + Innoextract + + + Stream error while extracting files! +error reason: + + + + + Not a supported Inno Setup installer! + + + + + VCMI was compiled without innoextract support, which is needed to extract exe files! + + + Language diff --git a/lib/GameSettings.cpp b/lib/GameSettings.cpp index 8248159ae..780c79f43 100644 --- a/lib/GameSettings.cpp +++ b/lib/GameSettings.cpp @@ -40,6 +40,7 @@ const std::vector GameSettings::settingProperties = {EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" }, {EGameSettings::BONUSES_GLOBAL, "bonuses", "global" }, {EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" }, + {EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" }, {EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" }, {EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" }, {EGameSettings::COMBAT_BAD_LUCK_DICE, "combat", "badLuckDice" }, diff --git a/lib/IGameSettings.h b/lib/IGameSettings.h index 049fdef23..9f6a8a78b 100644 --- a/lib/IGameSettings.h +++ b/lib/IGameSettings.h @@ -18,6 +18,7 @@ enum class EGameSettings BANKS_SHOW_GUARDS_COMPOSITION, BONUSES_GLOBAL, BONUSES_PER_HERO, + COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, COMBAT_ATTACK_POINT_DAMAGE_FACTOR, COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, COMBAT_BAD_LUCK_DICE, diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 5ccfe6838..b8d7e881f 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -17,6 +17,7 @@ #include "BattleInfo.h" #include "CObstacleInstance.h" #include "DamageCalculator.h" +#include "IGameSettings.h" #include "PossiblePlayerBattleAction.h" #include "../entities/building/TownFortifications.h" #include "../spells/ObstacleCasterProxy.h" @@ -725,18 +726,49 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker) const || attacker->hasBonusOfType(BonusType::FREE_SHOOTING)); } +bool CBattleInfoCallback::battleCanTargetEmptyHex(const battle::Unit * attacker) const +{ + RETURN_IF_NOT_BATTLE(false); + + if(!VLC->engineSettings()->getBoolean(EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX)) + return false; + + if(attacker->hasBonusOfType(BonusType::SPELL_LIKE_ATTACK)) + { + auto bonus = attacker->getBonus(Selector::type()(BonusType::SPELL_LIKE_ATTACK)); + const CSpell * spell = bonus->subtype.as().toSpell(); + spells::BattleCast cast(this, attacker, spells::Mode::SPELL_LIKE_ATTACK, spell); + BattleHex dummySpellTarget = BattleHex(50); //check arbitrary hex for general spell range since currently there is no general way to access amount of hexes + + if(spell->battleMechanics(&cast)->rangeInHexes(dummySpellTarget).size() > 1) + { + return true; + } + } + + return false; +} + bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker, BattleHex dest) const { RETURN_IF_NOT_BATTLE(false); const battle::Unit * defender = battleGetUnitByPos(dest); - if(!attacker || !defender) + if(!attacker) return false; - if(defender->hasBonusOfType(BonusType::INVINCIBLE)) - return false; + bool emptyHexAreaAttack = battleCanTargetEmptyHex(attacker); - if(battleMatchOwner(attacker, defender) && defender->alive()) + if(!emptyHexAreaAttack) + { + if(!defender) + return false; + + if(defender->hasBonusOfType(BonusType::INVINCIBLE)) + return false; + } + + if(emptyHexAreaAttack || (battleMatchOwner(attacker, defender) && defender->alive())) { if(battleCanShoot(attacker)) { @@ -747,7 +779,11 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker, BattleHe } int shootingRange = limitedRangeBonus->val; - return isEnemyUnitWithinSpecifiedRange(attacker->getPosition(), defender, shootingRange); + + if(defender) + return isEnemyUnitWithinSpecifiedRange(attacker->getPosition(), defender, shootingRange); + else + return isHexWithinSpecifiedRange(attacker->getPosition(), dest, shootingRange); } } @@ -794,7 +830,7 @@ DamageEstimation CBattleInfoCallback::battleEstimateDamage(const BattleAttackInf if (!bai.defender->ableToRetaliate()) return ret; - if (bai.attacker->hasBonusOfType(BonusType::BLOCKS_RETALIATION)) + if (bai.attacker->hasBonusOfType(BonusType::BLOCKS_RETALIATION) || bai.attacker->hasBonusOfType(BonusType::INVINCIBLE)) return ret; //TODO: rewrite using boost::numeric::interval @@ -1593,6 +1629,14 @@ bool CBattleInfoCallback::isEnemyUnitWithinSpecifiedRange(BattleHex attackerPosi return false; } +bool CBattleInfoCallback::isHexWithinSpecifiedRange(BattleHex attackerPosition, BattleHex targetPosition, unsigned int range) const +{ + if(BattleHex::getDistance(attackerPosition, targetPosition) <= range) + return true; + + return false; +} + BattleHex CBattleInfoCallback::wallPartToBattleHex(EWallPart part) const { RETURN_IF_NOT_BATTLE(BattleHex::INVALID); diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index 6e42f3d94..aedba1996 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -86,9 +86,11 @@ public: ReachabilityInfo::TDistances battleGetDistances(const battle::Unit * unit, BattleHex assumedPosition) const; std::set battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; bool isEnemyUnitWithinSpecifiedRange(BattleHex attackerPosition, const battle::Unit * defenderUnit, unsigned int range) const; + bool isHexWithinSpecifiedRange(BattleHex attackerPosition, BattleHex targetPosition, unsigned int range) const; std::pair< std::vector, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const; + bool battleCanTargetEmptyHex(const battle::Unit * attacker) const; //determines of stack with given ID can target empty hex to attack - currently used only for SPELL_LIKE_ATTACK shooting bool battleCanAttack(const battle::Unit * stack, const battle::Unit * target, BattleHex dest) const; //determines if stack with given ID can attack target at the selected destination bool battleCanShoot(const battle::Unit * attacker, BattleHex dest) const; //determines if stack with given ID shoot at the selected destination bool battleCanShoot(const battle::Unit * attacker) const; //determines if stack with given ID shoot in principle diff --git a/lib/campaign/CampaignState.cpp b/lib/campaign/CampaignState.cpp index 2e628107b..37ae497ef 100644 --- a/lib/campaign/CampaignState.cpp +++ b/lib/campaign/CampaignState.cpp @@ -37,8 +37,11 @@ CampaignRegions::RegionDescription CampaignRegions::RegionDescription::fromJson( { CampaignRegions::RegionDescription rd; rd.infix = node["infix"].String(); - rd.xpos = static_cast(node["x"].Float()); - rd.ypos = static_cast(node["y"].Float()); + rd.pos = Point(static_cast(node["x"].Float()), static_cast(node["y"].Float())); + if(!node["labelPos"].isNull()) + rd.labelPos = Point(static_cast(node["labelPos"]["x"].Float()), static_cast(node["labelPos"]["y"].Float())); + else + rd.labelPos = std::nullopt; return rd; } @@ -80,7 +83,13 @@ ImagePath CampaignRegions::getBackgroundName() const Point CampaignRegions::getPosition(CampaignScenarioID which) const { auto const & region = regions[which.getNum()]; - return Point(region.xpos, region.ypos); + return region.pos; +} + +std::optional CampaignRegions::getLabelPosition(CampaignScenarioID which) const +{ + auto const & region = regions[which.getNum()]; + return region.labelPos; } ImagePath CampaignRegions::getNameFor(CampaignScenarioID which, int colorIndex, std::string type) const diff --git a/lib/campaign/CampaignState.h b/lib/campaign/CampaignState.h index 67e027354..c4103c128 100644 --- a/lib/campaign/CampaignState.h +++ b/lib/campaign/CampaignState.h @@ -16,6 +16,7 @@ #include "CampaignConstants.h" #include "CampaignScenarioPrologEpilog.h" #include "../gameState/HighScore.h" +#include "../Point.h" VCMI_LIB_NAMESPACE_BEGIN @@ -27,7 +28,6 @@ class CMap; class CMapHeader; class CMapInfo; class JsonNode; -class Point; class IGameCallback; class DLL_LINKAGE CampaignRegions @@ -40,14 +40,22 @@ class DLL_LINKAGE CampaignRegions struct DLL_LINKAGE RegionDescription { std::string infix; - int xpos; - int ypos; + Point pos; + std::optional labelPos; template void serialize(Handler &h) { h & infix; - h & xpos; - h & ypos; + if (h.version >= Handler::Version::REGION_LABEL) + { + h & pos; + h & labelPos; + } + else + { + h & pos.x; + h & pos.y; + } } static CampaignRegions::RegionDescription fromJson(const JsonNode & node); @@ -60,6 +68,7 @@ class DLL_LINKAGE CampaignRegions public: ImagePath getBackgroundName() const; Point getPosition(CampaignScenarioID which) const; + std::optional getLabelPosition(CampaignScenarioID which) const; ImagePath getAvailableName(CampaignScenarioID which, int color) const; ImagePath getSelectedName(CampaignScenarioID which, int color) const; ImagePath getConqueredName(CampaignScenarioID which, int color) const; diff --git a/lib/serializer/ESerializationVersion.h b/lib/serializer/ESerializationVersion.h index 3a835ef1e..dd0deb6b0 100644 --- a/lib/serializer/ESerializationVersion.h +++ b/lib/serializer/ESerializationVersion.h @@ -60,6 +60,7 @@ enum class ESerializationVersion : int32_t PER_MAP_GAME_SETTINGS, // 861 - game settings are now stored per-map CAMPAIGN_OUTRO_SUPPORT, // 862 - support for campaign outro video REWARDABLE_BANKS, // 863 - team state contains list of scouted objects, coast visitable rewardable objects + REGION_LABEL, // 864 - labels for campaign regions - CURRENT = REWARDABLE_BANKS + CURRENT = REGION_LABEL }; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index a1f588277..4d79fd0e3 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -231,7 +231,7 @@ bool BattleSpellMechanics::canBeCastAt(const Target & target, Problem & problem) if(mainTarget && mainTarget == caster) return false; // can't cast on self - if(mainTarget && mainTarget->hasBonusOfType(BonusType::INVINCIBLE)) + if(mainTarget && mainTarget->hasBonusOfType(BonusType::INVINCIBLE) && !getSpell()->getPositiveness()) return false; } diff --git a/lib/spells/effects/UnitEffect.cpp b/lib/spells/effects/UnitEffect.cpp index 3d1be5842..657f98e9f 100644 --- a/lib/spells/effects/UnitEffect.cpp +++ b/lib/spells/effects/UnitEffect.cpp @@ -164,10 +164,9 @@ EffectTarget UnitEffect::transformTargetByRange(const Mechanics * m, const Targe if(m->alwaysHitFirstTarget()) { + //TODO: examine if adjustments needed related to INVINCIBLE bonus if(!aimPoint.empty() && aimPoint.front().unitValue) targets.insert(aimPoint.front().unitValue); - else - logGlobal->error("Spell-like attack with no primary target."); } EffectTarget effectTarget; diff --git a/mapeditor/translation/chinese.ts b/mapeditor/translation/chinese.ts index 0b7aff0e6..a55371343 100644 --- a/mapeditor/translation/chinese.ts +++ b/mapeditor/translation/chinese.ts @@ -42,7 +42,7 @@ 移除 - + New event 新事件 @@ -359,7 +359,7 @@ - + View underground 查看地下 @@ -441,9 +441,9 @@ - - - + + + Update appearance 更新外观 @@ -594,72 +594,72 @@ 所有支持的地图类型(*.vmap *.h3m);;VCMI地图(*.vmap);;英雄无敌3地图(*.h3m) - + Save map 保存地图 - + VCMI maps (*.vmap) VCMI地图(*.vmap) - + Type 类型 - + View surface 查看地上 - + No objects selected 未选择任何物体 - + This operation is irreversible. Do you want to continue? 此操作无法被撤销,你确定要继续么? - + Errors occurred. %1 objects were not updated 发生错误!%1 物体未完成更新 - + Save to image 保存为图片 - + Select maps to convert 选择待转换的地图 - + HoMM3 maps(*.h3m) 英雄无敌3地图文件(*.h3m) - + Choose directory to save converted maps 选择保存转换地图的目录 - + Operation completed 操作完成 - + Successfully converted %1 maps 成功转换 %1 地图 - + Failed to convert the map. Abort operation 转换地图失败,操作终止 @@ -1574,12 +1574,12 @@ 确定 - + Creature level %1 / Creature level %1 Upgrade %1级生物 / 升级后的%1级生物 - + Day %1 - %2 %1 - %2 日 @@ -1951,7 +1951,7 @@ 玩家 - + 0 0 @@ -2050,42 +2050,62 @@ 岛屿 - + + Roads + 道路 + + + + Dirt + 泥土 + + + + Gravel + 砂砾 + + + + Cobblestone + 鹅卵石 + + + Template 模版 - + Custom seed 自定义种子 - + Generate random map 生成随机地图 - + Ok 确定 - + Cancel 取消 - + No template 缺少模版 - + No template for parameters specified. Random map cannot be generated. 未指定任一模版作为参数,随机地图无法生成。 - + RMG failure 随机地图生成失败 diff --git a/mapeditor/translation/czech.ts b/mapeditor/translation/czech.ts index b3491ae84..cfdc136c7 100644 --- a/mapeditor/translation/czech.ts +++ b/mapeditor/translation/czech.ts @@ -42,7 +42,7 @@ Odebrat - + New event Nová událost @@ -359,7 +359,7 @@ - + View underground Zobrazit podzemí @@ -441,9 +441,9 @@ - - - + + + Update appearance Aktualizovat vzhled @@ -594,72 +594,72 @@ Všechny podporované mapy (*.vmap *.h3m);; Mapy VCMI(*.vmap);;Mapy HoMM3(*.h3m) - + Save map Uložit mapu - + VCMI maps (*.vmap) Mapy VCMI (*.vmap) - + Type Druh - + View surface Zobrazit povrch - + No objects selected Nejsou vybrány žádné objekty - + This operation is irreversible. Do you want to continue? Tento úkon je nezvratný. Chcete pokračovat? - + Errors occurred. %1 objects were not updated Nastaly chyby. Nebylo aktualizováno %1 objektů - + Save to image Uložit do obrázku - + Select maps to convert Vyberte mapy pro převod - + HoMM3 maps(*.h3m) Mapy HoMM3 (*.h3m) - + Choose directory to save converted maps Vyberte složku pro uložení převedených map - + Operation completed Operace dokončena - + Successfully converted %1 maps Úspěšně převedeno %1 map - + Failed to convert the map. Abort operation Převod map selhal. Úkon zrušen @@ -1574,12 +1574,12 @@ - + Creature level %1 / Creature level %1 Upgrade - + Day %1 - %2 @@ -1951,7 +1951,7 @@ Hráči - + 0 0 @@ -2050,42 +2050,62 @@ Ostrovy - + + Roads + Cesty + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Šablona - + Custom seed Vlastní semínko - + Generate random map Vygenerovat náhodnou mapu - + Ok Dobře - + Cancel Zrušit - + No template Bez šablony - + No template for parameters specified. Random map cannot be generated. Žádná šablona pro vybrané parametry. Náhodná mapa nemůže být vygenerována. - + RMG failure Chyba RMG diff --git a/mapeditor/translation/english.ts b/mapeditor/translation/english.ts index a5450b4de..2eec9132d 100644 --- a/mapeditor/translation/english.ts +++ b/mapeditor/translation/english.ts @@ -42,7 +42,7 @@ - + New event @@ -359,7 +359,7 @@ - + View underground @@ -441,9 +441,9 @@ - - - + + + Update appearance @@ -594,72 +594,72 @@ - + Save map - + VCMI maps (*.vmap) - + Type - + View surface - + No objects selected - + This operation is irreversible. Do you want to continue? - + Errors occurred. %1 objects were not updated - + Save to image - + Select maps to convert - + HoMM3 maps(*.h3m) - + Choose directory to save converted maps - + Operation completed - + Successfully converted %1 maps - + Failed to convert the map. Abort operation @@ -1574,12 +1574,12 @@ - + Creature level %1 / Creature level %1 Upgrade - + Day %1 - %2 @@ -1951,7 +1951,7 @@ - + 0 @@ -2050,42 +2050,62 @@ - + + Roads + + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template - + Custom seed - + Generate random map - + Ok - + Cancel - + No template - + No template for parameters specified. Random map cannot be generated. - + RMG failure diff --git a/mapeditor/translation/french.ts b/mapeditor/translation/french.ts index 7977d4715..919ed20b9 100644 --- a/mapeditor/translation/french.ts +++ b/mapeditor/translation/french.ts @@ -42,7 +42,7 @@ Supprimer - + New event Nouvel évènement @@ -359,7 +359,7 @@ - + View underground Voir le sous-sol @@ -441,9 +441,9 @@ - - - + + + Update appearance Mettre à jour l'apparence @@ -594,72 +594,72 @@ Toutes les cartes prises en charge (*.vmap *.h3m);;Cartes VCMI (*.vmap);;Cartes HoMM3 (*.h3m) - + Save map Enregistrer la carte - + VCMI maps (*.vmap) Cartes VCMI (*.vmap) - + Type Type - + View surface Afficher la surface - + No objects selected Pas d'objets sélectionnés - + This operation is irreversible. Do you want to continue? Cette opération est irreversible. Voulez-vous continuer ? - + Errors occurred. %1 objects were not updated Erreur rencontrée. %1 objets n'ont pas étés mis à jour - + Save to image Sauvegarder en tant qu'image - + Select maps to convert Sélectionner les cartes à convertir - + HoMM3 maps(*.h3m) Cartes HoMM3(*.h3m) - + Choose directory to save converted maps Sélectionner le dossier ou sauvegarder les cartes converties - + Operation completed Opération terminée - + Successfully converted %1 maps Conversion éffectuée avec succès des %1 cartes - + Failed to convert the map. Abort operation Erreur de conversion de carte. Opération annulée @@ -1574,12 +1574,12 @@ OK - + Creature level %1 / Creature level %1 Upgrade Créature niveau %1 / Créature niveau %1 Augmenté - + Day %1 - %2 Jour %1 - %2 @@ -1649,8 +1649,8 @@ - Sort qui doit apparaitre dans la Guilde des Mages - + Spell that must appear in mage guild + @@ -1951,7 +1951,7 @@ Joueurs - + 0 0 @@ -2050,42 +2050,62 @@ Îles - + + Roads + Routes + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Modèle - + Custom seed Graine personnalisée - + Generate random map Générer une carte aléatoire - + Ok OK - + Cancel Annuler - + No template Pas de modèle - + No template for parameters specified. Random map cannot be generated. Pas de modèles pour les paramètres spécifiés. La carte aléatoire ne peut pas être générée. - + RMG failure Echec de RMG diff --git a/mapeditor/translation/german.ts b/mapeditor/translation/german.ts index 2ce928427..eebafd7ee 100644 --- a/mapeditor/translation/german.ts +++ b/mapeditor/translation/german.ts @@ -42,7 +42,7 @@ Entfernen - + New event Neues Ereignis @@ -359,7 +359,7 @@ - + View underground Ansicht Untergrund @@ -441,9 +441,9 @@ - - - + + + Update appearance Aussehen aktualisieren @@ -594,72 +594,72 @@ Alle unterstützten Karten (*.vmap *.h3m);;VCMI-Karten (*.vmap);;HoMM3-Karten (*.h3m) - + Save map Karte speichern - + VCMI maps (*.vmap) VCMI-Karten (*.vmap) - + Type Typ - + View surface Oberfläche anzeigen - + No objects selected Keine Objekte selektiert - + This operation is irreversible. Do you want to continue? Diese Operation ist unumkehrbar. Möchten sie fortsetzen? - + Errors occurred. %1 objects were not updated Fehler sind aufgetreten. %1 Objekte konnten nicht aktualisiert werden - + Save to image Als Bild speichern - + Select maps to convert Zu konvertierende Karten auswählen - + HoMM3 maps(*.h3m) HoMM3-Karten (*.h3m) - + Choose directory to save converted maps Verzeichnis zum Speichern der konvertierten Karten wählen - + Operation completed Vorgang abgeschlossen - + Successfully converted %1 maps Erfolgreiche Konvertierung von %1 Karten - + Failed to convert the map. Abort operation Die Karte konnte nicht konvertiert werden. Vorgang abgebrochen @@ -1574,12 +1574,12 @@ - + Creature level %1 / Creature level %1 Upgrade - + Day %1 - %2 @@ -1951,7 +1951,7 @@ Spieler - + 0 0 @@ -2050,42 +2050,62 @@ Inseln - + + Roads + Straßen + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Vorlage - + Custom seed Benutzerdefiniertes Seed - + Generate random map Zufällige Karte generieren - + Ok Ok - + Cancel Abbrechen - + No template Kein Template - + No template for parameters specified. Random map cannot be generated. Es wurde kein Template für Parameter erstellt. Zufällige Karte kann nicht generiert werden. - + RMG failure RMG-Fehler diff --git a/mapeditor/translation/polish.ts b/mapeditor/translation/polish.ts index 1b227303e..4d65128c2 100644 --- a/mapeditor/translation/polish.ts +++ b/mapeditor/translation/polish.ts @@ -42,7 +42,7 @@ Usuń - + New event Nowe zdarzenie @@ -359,7 +359,7 @@ - + View underground Pokaż podziemia @@ -441,9 +441,9 @@ - - - + + + Update appearance Aktualizuj wygląd @@ -594,72 +594,72 @@ Wszystkie wspierane mapy (*.vmap *.h3m);;Mapy VCMI(*.vmap);;Mapy HoMM3(*.h3m) - + Save map Zapisz mapę - + VCMI maps (*.vmap) Mapy VCMI (*.vmap) - + Type Typ - + View surface Pokaż powierzchnię - + No objects selected Brak wybranych obiektów - + This operation is irreversible. Do you want to continue? Ta operacja jest nieodwracalna. Czy chcesz kontynuować? - + Errors occurred. %1 objects were not updated Wystąpiły błędy. %1 obiektów nie zostało zaktualizowanych - + Save to image Zapisz jako obraz - + Select maps to convert Wybierz mapy do konwersji - + HoMM3 maps(*.h3m) Mapy HoMM3(*.h3m) - + Choose directory to save converted maps Wybierz folder zapisu skonwertowanych map - + Operation completed Operacja zakończona - + Successfully converted %1 maps Pomyślnie skonwertowano %1 map - + Failed to convert the map. Abort operation Nieudana konwersja mapy. Przerywanie operacji @@ -1574,12 +1574,12 @@ OK - + Creature level %1 / Creature level %1 Upgrade Stworzenie poziomu %1 / Ulepszone stworzenie poziomu %1 - + Day %1 - %2 Dzień %1 - %2 @@ -1951,7 +1951,7 @@ Gracze - + 0 0 @@ -2050,42 +2050,62 @@ Wyspy - + + Roads + Drogi + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Szablon - + Custom seed Własny seed - + Generate random map Generuj mapę losową - + Ok Ok - + Cancel Anuluj - + No template Brak szablonu - + No template for parameters specified. Random map cannot be generated. Brak szablonu dla wybranych parametrów. Mapa losowa nie może zostać wygenerowana. - + RMG failure Niepowodzenie generatora map losowych diff --git a/mapeditor/translation/portuguese.ts b/mapeditor/translation/portuguese.ts index 1644feaf7..668d74a12 100644 --- a/mapeditor/translation/portuguese.ts +++ b/mapeditor/translation/portuguese.ts @@ -42,7 +42,7 @@ Remover - + New event Novo evento @@ -359,7 +359,7 @@ - + View underground Visualizar subterrâneo @@ -441,9 +441,9 @@ - - - + + + Update appearance Atualizar aparência @@ -594,72 +594,72 @@ Todos os mapas suportados (*.vmap *.h3m);;Mapas do VCMI (*.vmap);;Mapas do HoMM3 (*.h3m) - + Save map Salvar mapa - + VCMI maps (*.vmap) Mapas do VCMI (*.vmap) - + Type Tipo - + View surface Visualizar superfície - + No objects selected Nenhum objeto selecionado - + This operation is irreversible. Do you want to continue? Esta operação é irreversível. Deseja continuar? - + Errors occurred. %1 objects were not updated Ocorreram erros. %1 objetos não foram atualizados - + Save to image Salvar como imagem - + Select maps to convert Selecionar mapas para converter - + HoMM3 maps(*.h3m) Mapas do HoMM3 (*.h3m) - + Choose directory to save converted maps Escolher diretório para salvar mapas convertidos - + Operation completed Operação concluída - + Successfully converted %1 maps %1 mapas foram convertidos com sucesso - + Failed to convert the map. Abort operation Falha ao converter o mapa. Abortar operação @@ -1574,12 +1574,12 @@ OK - + Creature level %1 / Creature level %1 Upgrade Nível da criatura %1 / Nível da criatura %1 - Atualização - + Day %1 - %2 Dia %1 - %2 @@ -1951,7 +1951,7 @@ Jogadores - + 0 0 @@ -2050,42 +2050,62 @@ Ilhas - + + Roads + Estradas + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Modelo - + Custom seed Semente personalizada - + Generate random map Gerar mapa aleatório - + Ok Ok - + Cancel Cancelar - + No template Sem modelo - + No template for parameters specified. Random map cannot be generated. Sem modelo para os parâmetros especificados. O mapa aleatório não pode set gerado. - + RMG failure Falha do GMA diff --git a/mapeditor/translation/russian.ts b/mapeditor/translation/russian.ts index c4955b60c..c0f54b7a7 100644 --- a/mapeditor/translation/russian.ts +++ b/mapeditor/translation/russian.ts @@ -42,7 +42,7 @@ - + New event @@ -359,7 +359,7 @@ - + View underground Вид на подземелье @@ -441,9 +441,9 @@ - - - + + + Update appearance Обновить вид @@ -594,72 +594,72 @@ Все поддерживаемые карты (*.vmap *.h3m);;Карты VCMI (*.vmap);;Карты Героев III (*.h3m) - + Save map Сохранить карту - + VCMI maps (*.vmap) Карты VCMI (*.vmap) - + Type Тип - + View surface Вид на поверхность - + No objects selected - + This operation is irreversible. Do you want to continue? - + Errors occurred. %1 objects were not updated - + Save to image - + Select maps to convert - + HoMM3 maps(*.h3m) - + Choose directory to save converted maps - + Operation completed - + Successfully converted %1 maps - + Failed to convert the map. Abort operation @@ -1574,12 +1574,12 @@ - + Creature level %1 / Creature level %1 Upgrade - + Day %1 - %2 @@ -1951,7 +1951,7 @@ Игроки - + 0 0 @@ -2050,42 +2050,62 @@ Острова - + + Roads + Дороги + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Шаблон - + Custom seed Пользовательское зерно - + Generate random map Сгенерировать случайную карту - + Ok ОК - + Cancel Отмена - + No template - + No template for parameters specified. Random map cannot be generated. - + RMG failure diff --git a/mapeditor/translation/spanish.ts b/mapeditor/translation/spanish.ts index d11074098..bd6340e4f 100644 --- a/mapeditor/translation/spanish.ts +++ b/mapeditor/translation/spanish.ts @@ -42,7 +42,7 @@ - + New event @@ -359,7 +359,7 @@ - + View underground Ver subterráneo @@ -441,9 +441,9 @@ - - - + + + Update appearance Actualizar apariencia @@ -594,72 +594,72 @@ Todos los mapas soportados (*.vmap *.h3m);;Mapas VCMI (*.vmap);;Mapas HoMM3 (*.h3m) - + Save map Guardar mapa - + VCMI maps (*.vmap) Mapas VCMI (*.vmap) - + Type Tipo - + View surface Ver superficie - + No objects selected - + This operation is irreversible. Do you want to continue? - + Errors occurred. %1 objects were not updated - + Save to image - + Select maps to convert - + HoMM3 maps(*.h3m) - + Choose directory to save converted maps - + Operation completed - + Successfully converted %1 maps - + Failed to convert the map. Abort operation @@ -1574,12 +1574,12 @@ - + Creature level %1 / Creature level %1 Upgrade - + Day %1 - %2 @@ -1951,7 +1951,7 @@ Jugadores - + 0 0 @@ -2050,42 +2050,62 @@ Islas - + + Roads + Caminos + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Plantilla - + Custom seed Semilla personalizada - + Generate random map Generar un mapa aleatorio - + Ok Aceptar - + Cancel Cancelar - + No template - + No template for parameters specified. Random map cannot be generated. - + RMG failure diff --git a/mapeditor/translation/ukrainian.ts b/mapeditor/translation/ukrainian.ts index 287605479..bb7d4e795 100644 --- a/mapeditor/translation/ukrainian.ts +++ b/mapeditor/translation/ukrainian.ts @@ -42,7 +42,7 @@ - + New event @@ -359,7 +359,7 @@ - + View underground Дивитись підземелля @@ -441,9 +441,9 @@ - - - + + + Update appearance Оновити вигляд @@ -594,72 +594,72 @@ Всі підтримувані мапи (*.vmap *.h3m);;Мапи VCMI (*.vmap);;Мапи HoMM3 (*.h3m) - + Save map Зберегти мапу - + VCMI maps (*.vmap) Мапи VCMI - + Type Тип - + View surface Дивитись поверхню - + No objects selected - + This operation is irreversible. Do you want to continue? - + Errors occurred. %1 objects were not updated - + Save to image - + Select maps to convert - + HoMM3 maps(*.h3m) - + Choose directory to save converted maps - + Operation completed - + Successfully converted %1 maps - + Failed to convert the map. Abort operation @@ -1574,12 +1574,12 @@ - + Creature level %1 / Creature level %1 Upgrade - + Day %1 - %2 @@ -1951,7 +1951,7 @@ Гравців - + 0 0 @@ -2050,42 +2050,62 @@ Острови - + + Roads + Шляхи + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Шаблон - + Custom seed Користувацьке зерно - + Generate random map Згенерувати випадкову карту - + Ok Підтвердити - + Cancel Скасувати - + No template - + No template for parameters specified. Random map cannot be generated. - + RMG failure diff --git a/mapeditor/translation/vietnamese.ts b/mapeditor/translation/vietnamese.ts index d00f4bd9f..825f16c59 100644 --- a/mapeditor/translation/vietnamese.ts +++ b/mapeditor/translation/vietnamese.ts @@ -42,7 +42,7 @@ - + New event @@ -359,7 +359,7 @@ - + View underground Xem hang ngầm @@ -441,9 +441,9 @@ - - - + + + Update appearance Cập nhật hiện thị @@ -594,72 +594,72 @@ Tất cả bản đồ hỗ trợ (*.vmap *.h3m);;Bản đồ VCMI (*.vmap);;Bản đồ HoMM3 (*.h3m) - + Save map Lưu bản đồ - + VCMI maps (*.vmap) Bản đồ VCMI (*.vmap) - + Type Loại - + View surface Xem bề mặt - + No objects selected Không mục tiêu được chọn - + This operation is irreversible. Do you want to continue? Thao tác này không thể đảo ngược. Bạn muốn tiếp tục? - + Errors occurred. %1 objects were not updated Xảy ra lỗi. %1 mục tiêu không được cập nhật - + Save to image Lưu thành ảnh - + Select maps to convert - + HoMM3 maps(*.h3m) - + Choose directory to save converted maps - + Operation completed - + Successfully converted %1 maps - + Failed to convert the map. Abort operation @@ -1574,12 +1574,12 @@ - + Creature level %1 / Creature level %1 Upgrade - + Day %1 - %2 @@ -1951,7 +1951,7 @@ Người chơi - + 0 0 @@ -2050,42 +2050,62 @@ Các đảo - + + Roads + Đường + + + + Dirt + + + + + Gravel + + + + + Cobblestone + + + + Template Mẫu - + Custom seed Tùy chỉnh ban đầu - + Generate random map Tạo bản đồ ngẫu nhiên - + Ok Đồng ý - + Cancel Hủy - + No template Không dùng mẫu - + No template for parameters specified. Random map cannot be generated. Không có mẫu cho tham số chỉ định. Bản đồ ngẫu nhiên không thể tạo - + RMG failure Tạo bản đồ ngẫu nhiên thất bại diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index d23ca699e..2ab7665e1 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -276,7 +276,7 @@ bool BattleActionProcessor::doAttackAction(const CBattleInfoCallback & battle, c for (int i = 0; i < totalAttacks; ++i) { //first strike - if(i == 0 && firstStrike && retaliation && !stack->hasBonusOfType(BonusType::BLOCKS_RETALIATION)) + if(i == 0 && firstStrike && retaliation && !stack->hasBonusOfType(BonusType::BLOCKS_RETALIATION) && !stack->hasBonusOfType(BonusType::INVINCIBLE)) { makeAttack(battle, destinationStack, stack, 0, stack->getPosition(), true, false, true); } @@ -303,6 +303,7 @@ bool BattleActionProcessor::doAttackAction(const CBattleInfoCallback & battle, c //we check retaliation twice, so if it unblocked during attack it will work only on next attack if(stack->alive() && !stack->hasBonusOfType(BonusType::BLOCKS_RETALIATION) + && !stack->hasBonusOfType(BonusType::INVINCIBLE) && (i == 0 && !firstStrike) && retaliation && destinationStack->ableToRetaliate()) { @@ -347,20 +348,27 @@ bool BattleActionProcessor::doShootAction(const CBattleInfoCallback & battle, co return false; } - if (!destinationStack) + const bool emptyTileAreaAttack = battle.battleCanTargetEmptyHex(stack); + + if (!destinationStack && !emptyTileAreaAttack) { gameHandler->complain("No target to shoot!"); return false; } - static const auto firstStrikeSelector = Selector::typeSubtype(BonusType::FIRST_STRIKE, BonusCustomSubtype::damageTypeAll).Or(Selector::typeSubtype(BonusType::FIRST_STRIKE, BonusCustomSubtype::damageTypeRanged)); - const bool firstStrike = destinationStack->hasBonus(firstStrikeSelector); + bool firstStrike = false; + if(!emptyTileAreaAttack) + { + static const auto firstStrikeSelector = Selector::typeSubtype(BonusType::FIRST_STRIKE, BonusCustomSubtype::damageTypeAll).Or(Selector::typeSubtype(BonusType::FIRST_STRIKE, BonusCustomSubtype::damageTypeRanged)); + firstStrike = destinationStack->hasBonus(firstStrikeSelector); + } if (!firstStrike) makeAttack(battle, stack, destinationStack, 0, destination, true, true, false); //ranged counterattack - if (destinationStack->hasBonusOfType(BonusType::RANGED_RETALIATION) + if (!emptyTileAreaAttack + && destinationStack->hasBonusOfType(BonusType::RANGED_RETALIATION) && !stack->hasBonusOfType(BonusType::BLOCKS_RANGED_RETALIATION) && destinationStack->ableToRetaliate() && battle.battleCanShoot(destinationStack, stack->getPosition()) @@ -381,11 +389,9 @@ bool BattleActionProcessor::doShootAction(const CBattleInfoCallback & battle, co for(int i = firstStrike ? 0:1; i < totalRangedAttacks; ++i) { - if( - stack->alive() - && destinationStack->alive() - && stack->shots.canUse() - ) + if(stack->alive() + && (emptyTileAreaAttack || destinationStack->alive()) + && stack->shots.canUse()) { makeAttack(battle, stack, destinationStack, 0, destination, false, true, false); } @@ -907,7 +913,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter) { - if(first && !counter) + if(defender && first && !counter) handleAttackBeforeCasting(battle, ranged, attacker, defender); FireShieldInfo fireShield; @@ -962,7 +968,7 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const battle::HealInfo healInfo; // only primary target - if(defender->alive()) + if(defender && defender->alive()) applyBattleEffects(battle, bat, attackerState, fireShield, defender, healInfo, distance, false); //multiple-hex normal attack @@ -1044,7 +1050,8 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const addGenericDamageLog(blm, attackerState, totalDamage); - addGenericKilledLog(blm, defender, totalKills, multipleTargets); + if(defender) + addGenericKilledLog(blm, defender, totalKills, multipleTargets); } // drain life effect (as well as log entry) must be applied after the attack @@ -1110,7 +1117,8 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const gameHandler->sendAndApply(&blm); - handleAfterAttackCasting(battle, ranged, attacker, defender); + if(defender) + handleAfterAttackCasting(battle, ranged, attacker, defender); } void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const CStack * defender)