From fdf7fab2d46f75370d2b1797085ed8ac9d7c85fb Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sun, 2 Apr 2023 13:58:03 +0300 Subject: [PATCH 01/34] [Conan][docs] improve using prebuilt binaries for Apple Silicon --- docs/conan.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/conan.md b/docs/conan.md index 063ed2200..352085f5d 100644 --- a/docs/conan.md +++ b/docs/conan.md @@ -35,7 +35,10 @@ The following platforms are supported and known to work, others might require ch if you want x86, otherwise pick **vcmi-deps-windows-conan.tgz** - [Android](https://github.com/vcmi/vcmi-dependencies/releases) -3. Only if you have Apple Silicon Mac and trying to build for macOS or iOS: follow [instructions how to build Qt host tools for Apple Silicon](https://github.com/vcmi/vcmi-ios-deps#note-for-arm-macs), on step 3 copy them to `~/.conan/data/qt/5.15.x/_/_/package/SOME_HASH/bin` (`5.15.x` and `SOME_HASH` are placeholders). +3. Only if you have Apple Silicon Mac and trying to build for macOS or iOS: + + 1. Open file `~/.conan/data/qt/5.15.x/_/_/export/conanfile.py` (`5.15.x` is a placeholder), search for string `Designer` (there should be only one match), comment this line and the one above it by inserting `#` in the beginning, and save the file. + 2. (optional) If you don't want to use Rosetta, follow [instructions how to build Qt host tools for Apple Silicon](https://github.com/vcmi/vcmi-ios-deps#note-for-arm-macs), on step 3 copy them to `~/.conan/data/qt/5.15.x/_/_/package/SOME_HASH/bin` (`5.15.x` and `SOME_HASH` are placeholders). Make sure **not** to copy `qt.conf`! ## Generate CMake integration From fc750d0917874c3f7e486b18432f865cc825d0b3 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Apr 2023 17:12:40 +0300 Subject: [PATCH 02/34] Fix identifiers resolving for stack experience bonuses --- lib/CCreatureHandler.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 3292317f6..7ac3e6f1b 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -963,9 +963,6 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode { for (const JsonNode &exp : input.Vector()) { - auto bonus = JsonUtils::parseBonus (exp["bonus"]); - bonus->source = Bonus::STACK_EXPERIENCE; - bonus->duration = Bonus::PERMANENT; const JsonVector &values = exp["values"].Vector(); int lowerLimit = 1;//, upperLimit = 255; if (values[0].getType() == JsonNode::JsonType::DATA_BOOL) @@ -974,8 +971,14 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode { if(val.Bool()) { + // parse each bonus separately + // we can not create copies since identifiers resolution does not tracks copies + // leading to unset identifier values in copies + auto bonus = JsonUtils::parseBonus (exp["bonus"]); + bonus->source = Bonus::STACK_EXPERIENCE; + bonus->duration = Bonus::PERMANENT; bonus->limiter = std::make_shared(RankRangeLimiter(lowerLimit)); - creature->addNewBonus (std::make_shared(*bonus)); //bonuses must be unique objects + creature->addNewBonus (bonus); break; //TODO: allow bonuses to turn off? } ++lowerLimit; @@ -988,9 +991,14 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode { if (val.Float() != lastVal) { - bonus->val = static_cast(val.Float()) - lastVal; + JsonNode bonusInput = exp["bonus"]; + bonusInput["val"].Float() = static_cast(val.Float()) - lastVal; + + auto bonus = JsonUtils::parseBonus (bonusInput); + bonus->source = Bonus::STACK_EXPERIENCE; + bonus->duration = Bonus::PERMANENT; bonus->limiter.reset (new RankRangeLimiter(lowerLimit)); - creature->addNewBonus (std::make_shared(*bonus)); + creature->addNewBonus (bonus); } lastVal = static_cast(val.Float()); ++lowerLimit; From c5225aab7010c78fdbc08fc5c8d2240a31e6061e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Apr 2023 22:22:58 +0300 Subject: [PATCH 03/34] Fix serialization --- lib/serializer/CSerializer.h | 2 +- lib/spells/CSpellHandler.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 55593e920..ec6207511 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN -const ui32 SERIALIZATION_VERSION = 820; +const ui32 SERIALIZATION_VERSION = 821; const ui32 MINIMAL_SERIALIZATION_VERSION = 820; const std::string SAVEGAME_MAGIC = "VCMISVG"; diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index c00e40559..b2cf45f0a 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -272,6 +272,8 @@ public: template void serialize(Handler & h, const int version) { h & identifier; + if (version > 820) + h & modScope; h & id; h & level; h & power; From 0770a1a15305b9011a0c2a967858566f0dc1d15a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Apr 2023 22:23:32 +0300 Subject: [PATCH 04/34] TTF fonts can now be used as fallback for H3 fonts --- client/render/IFont.cpp | 1 - client/renderSDL/CBitmapFont.cpp | 20 +++++++++++++++++++- client/renderSDL/CBitmapFont.h | 5 +++++ client/renderSDL/CTrueTypeFont.cpp | 29 ++++++++++++++++++++++++++--- client/renderSDL/CTrueTypeFont.h | 5 +++++ 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/client/render/IFont.cpp b/client/render/IFont.cpp index 48a84cab4..2151b542a 100644 --- a/client/render/IFont.cpp +++ b/client/render/IFont.cpp @@ -13,7 +13,6 @@ #include "../../lib/Point.h" #include "../../lib/TextOperations.h" -// size_t IFont::getStringWidth(const std::string & data) const { diff --git a/client/renderSDL/CBitmapFont.cpp b/client/renderSDL/CBitmapFont.cpp index 93753f70c..0810b649b 100644 --- a/client/renderSDL/CBitmapFont.cpp +++ b/client/renderSDL/CBitmapFont.cpp @@ -70,7 +70,7 @@ CBitmapFont::CBitmapFont(const std::string & filename): loadModFont("core", resource); - for (auto const & modName : VLC->modh->getActiveMods()) + for(const auto & modName : VLC->modh->getActiveMods()) { if (CResourceHandler::get(modName)->existsResource(resource)) loadModFont(modName, resource); @@ -94,6 +94,24 @@ size_t CBitmapFont::getGlyphWidth(const char * data) const return iter->second.leftOffset + iter->second.width + iter->second.rightOffset; } +bool CBitmapFont::canRepresentCharacter(const char *data) const +{ + CodePoint localChar = TextOperations::getUnicodeCodepoint(data, 4); + + auto iter = chars.find(localChar); + + return iter != chars.end(); +} + +bool CBitmapFont::canRepresentString(const std::string & data) const +{ + for(size_t i=0; i(fallbackName); } +CTrueTypeFont::~CTrueTypeFont() = default; + size_t CTrueTypeFont::getLineHeight() const { + if (fallbackFont) + fallbackFont->getLineHeight(); + return TTF_FontHeight(font.get()); } size_t CTrueTypeFont::getGlyphWidth(const char *data) const { + if (fallbackFont && fallbackFont->canRepresentCharacter(data)) + return fallbackFont->getGlyphWidth(data); + return getStringWidth(std::string(data, TextOperations::getUnicodeCharacterSize(*data))); - /* int advance; TTF_GlyphMetrics(font.get(), *data, nullptr, nullptr, nullptr, nullptr, &advance); return advance; - */ } size_t CTrueTypeFont::getStringWidth(const std::string & data) const { + if (fallbackFont && fallbackFont->canRepresentString(data)) + return fallbackFont->getStringWidth(data); + int width; TTF_SizeUTF8(font.get(), data.c_str(), &width, nullptr); return width; @@ -83,7 +100,13 @@ size_t CTrueTypeFont::getStringWidth(const std::string & data) const void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const { - if (color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow + if (fallbackFont && fallbackFont->canRepresentString(data)) + { + fallbackFont->renderText(surface, data, color, pos); + return; + } + + if (dropShadow && color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow renderText(surface, data, Colors::BLACK, pos + Point(1,1)); if (!data.empty()) diff --git a/client/renderSDL/CTrueTypeFont.h b/client/renderSDL/CTrueTypeFont.h index eb834b40c..407d083f5 100644 --- a/client/renderSDL/CTrueTypeFont.h +++ b/client/renderSDL/CTrueTypeFont.h @@ -15,14 +15,18 @@ VCMI_LIB_NAMESPACE_BEGIN class JsonNode; VCMI_LIB_NAMESPACE_END +class CBitmapFont; + typedef struct _TTF_Font TTF_Font; class CTrueTypeFont : public IFont { + std::unique_ptr fallbackFont; const std::pair, ui64> data; const std::unique_ptr font; const bool blended; + const bool dropShadow; std::pair, ui64> loadData(const JsonNode & config); TTF_Font * loadFont(const JsonNode & config); @@ -31,6 +35,7 @@ class CTrueTypeFont : public IFont void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override; public: CTrueTypeFont(const JsonNode & fontConfig); + ~CTrueTypeFont(); size_t getLineHeight() const override; size_t getGlyphWidth(const char * data) const override; From cf966015bb7c9847c6ae08f0746f5b252bdbc030 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Apr 2023 18:35:45 +0300 Subject: [PATCH 05/34] Correctly update battle queue state after changing its state --- client/battle/BattleWindow.cpp | 68 +++++++++++--------- client/windows/settings/BattleOptionsTab.cpp | 15 +++-- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index b9d308764..bd520e67a 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -86,30 +86,34 @@ BattleWindow::BattleWindow(BattleInterface & owner): void BattleWindow::createQueue() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - - //create stack queue and adjust our own position - bool embedQueue; - std::string queueSize = settings["battle"]["queueSize"].String(); - - if(queueSize == "auto") - embedQueue = GH.screenDimensions().y < 700; - else - embedQueue = GH.screenDimensions().y < 700 || queueSize == "small"; - - queue = std::make_shared(embedQueue, owner); - if(!embedQueue && settings["battle"]["showQueue"].Bool()) - { - //re-center, taking into account stack queue position - pos.y -= queue->pos.h; - pos.h += queue->pos.h; - pos = center(); - } -} - -BattleWindow::~BattleWindow() -{ - CPlayerInterface::battleInt = nullptr; -} + + //create stack queue and adjust our own position + bool embedQueue; + bool showQueue = settings["battle"]["showQueue"].Bool(); + std::string queueSize = settings["battle"]["queueSize"].String(); + + if(queueSize == "auto") + embedQueue = GH.screenDimensions().y < 700; + else + embedQueue = GH.screenDimensions().y < 700 || queueSize == "small"; + + queue = std::make_shared(embedQueue, owner); + if(!embedQueue && showQueue) + { + //re-center, taking into account stack queue position + pos.y -= queue->pos.h; + pos.h += queue->pos.h; + pos = center(); + } + + if (!showQueue) + queue->disable(); +} + +BattleWindow::~BattleWindow() +{ + CPlayerInterface::battleInt = nullptr; +} std::shared_ptr BattleWindow::buildBattleConsole(const JsonNode & config) const { @@ -140,14 +144,14 @@ void BattleWindow::hideQueue() if (!queue->embedded) { //re-center, taking into account stack queue position - pos.y += queue->pos.h; - pos.h -= queue->pos.h; - pos = center(); - GH.totalRedraw(); - } -} - -void BattleWindow::showQueue() + pos.y += queue->pos.h; + pos.h -= queue->pos.h; + pos = center(); + } + GH.totalRedraw(); +} + +void BattleWindow::showQueue() { if(settings["battle"]["showQueue"].Bool() == true) return; diff --git a/client/windows/settings/BattleOptionsTab.cpp b/client/windows/settings/BattleOptionsTab.cpp index fdd82feb4..65b62341f 100644 --- a/client/windows/settings/BattleOptionsTab.cpp +++ b/client/windows/settings/BattleOptionsTab.cpp @@ -84,14 +84,19 @@ int BattleOptionsTab::getAnimSpeed() const int BattleOptionsTab::getQueueSizeId() const { - std::string text = settings["battle"]["queueSize"].String(); - if(text == "none") + std::string sizeText = settings["battle"]["queueSize"].String(); + bool visible = settings["battle"]["showQueue"].Bool(); + + if(!visible) return -1; - if(text == "auto") + + if(sizeText == "none") + return -1; + if(sizeText == "auto") return 0; - if(text == "small") + if(sizeText == "small") return 1; - if(text == "big") + if(sizeText == "big") return 2; return 0; From a3c5f7560045d44300c7d40d37e9bf2ed0c9ee4c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Apr 2023 18:36:10 +0300 Subject: [PATCH 06/34] Fix visibility of alternative actions button in tactics stage --- client/battle/BattleWindow.cpp | 78 ++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index bd520e67a..8ca01cef7 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -86,34 +86,34 @@ BattleWindow::BattleWindow(BattleInterface & owner): void BattleWindow::createQueue() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - - //create stack queue and adjust our own position - bool embedQueue; - bool showQueue = settings["battle"]["showQueue"].Bool(); - std::string queueSize = settings["battle"]["queueSize"].String(); - - if(queueSize == "auto") - embedQueue = GH.screenDimensions().y < 700; - else - embedQueue = GH.screenDimensions().y < 700 || queueSize == "small"; - - queue = std::make_shared(embedQueue, owner); - if(!embedQueue && showQueue) - { - //re-center, taking into account stack queue position - pos.y -= queue->pos.h; - pos.h += queue->pos.h; - pos = center(); - } - - if (!showQueue) - queue->disable(); -} - -BattleWindow::~BattleWindow() -{ - CPlayerInterface::battleInt = nullptr; -} + + //create stack queue and adjust our own position + bool embedQueue; + bool showQueue = settings["battle"]["showQueue"].Bool(); + std::string queueSize = settings["battle"]["queueSize"].String(); + + if(queueSize == "auto") + embedQueue = GH.screenDimensions().y < 700; + else + embedQueue = GH.screenDimensions().y < 700 || queueSize == "small"; + + queue = std::make_shared(embedQueue, owner); + if(!embedQueue && showQueue) + { + //re-center, taking into account stack queue position + pos.y -= queue->pos.h; + pos.h += queue->pos.h; + pos = center(); + } + + if (!showQueue) + queue->disable(); +} + +BattleWindow::~BattleWindow() +{ + CPlayerInterface::battleInt = nullptr; +} std::shared_ptr BattleWindow::buildBattleConsole(const JsonNode & config) const { @@ -144,14 +144,14 @@ void BattleWindow::hideQueue() if (!queue->embedded) { //re-center, taking into account stack queue position - pos.y += queue->pos.h; - pos.h -= queue->pos.h; - pos = center(); - } - GH.totalRedraw(); -} - -void BattleWindow::showQueue() + pos.y += queue->pos.h; + pos.h -= queue->pos.h; + pos = center(); + } + GH.totalRedraw(); +} + +void BattleWindow::showQueue() { if(settings["battle"]["showQueue"].Bool() == true) return; @@ -217,9 +217,12 @@ void BattleWindow::tacticPhaseStarted() auto menuTactics = widget("menuTactics"); auto tacticNext = widget("tacticNext"); auto tacticEnd = widget("tacticEnd"); + auto alternativeAction = widget("alternativeAction"); menuBattle->disable(); console->disable(); + if (alternativeAction) + alternativeAction->disable(); menuTactics->enable(); tacticNext->enable(); @@ -235,9 +238,12 @@ void BattleWindow::tacticPhaseEnded() auto menuTactics = widget("menuTactics"); auto tacticNext = widget("tacticNext"); auto tacticEnd = widget("tacticEnd"); + auto alternativeAction = widget("alternativeAction"); menuBattle->enable(); console->enable(); + if (alternativeAction) + alternativeAction->enable(); menuTactics->disable(); tacticNext->disable(); From 1da06e04cc3a752bbd0b710d54b071c76a46f9f8 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Wed, 5 Apr 2023 20:22:29 +0300 Subject: [PATCH 07/34] fix "development mode" condition when building in single process mode --- lib/VCMIDirs.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/VCMIDirs.cpp b/lib/VCMIDirs.cpp index 5c56dc54f..3c0692885 100644 --- a/lib/VCMIDirs.cpp +++ b/lib/VCMIDirs.cpp @@ -367,7 +367,12 @@ class IVCMIDirsUNIX : public IVCMIDirs bool IVCMIDirsUNIX::developmentMode() const { // We want to be able to run VCMI from single directory. E.g to run from build output directory - return bfs::exists("AI") && bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiserver") && bfs::exists("vcmiclient"); + const bool result = bfs::exists("AI") && bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiclient"); +#if SINGLE_PROCESS_APP + return result; +#else + return result && bfs::exists("vcmiserver"); +#endif } bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; } From dc7d0297a7a24b51bfe346cabf9192539bedf8c2 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Thu, 6 Apr 2023 00:21:21 +0300 Subject: [PATCH 08/34] CGameHandler: fixup healing destination If it contains hex, not unit, server complains. Add actual unit there. --- server/CGameHandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 18ba84335..e5d8d428c 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4898,8 +4898,9 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) { const CSpell * spell = SpellID(healerAbility->subtype).toSpell(); spells::BattleCast parameters(gs->curB, healer, spells::Mode::SPELL_LIKE_ATTACK, spell); //We can heal infinitely by first aid tent + auto dest = battle::Destination(destStack, target.at(0).hexValue); parameters.setSpellLevel(0); - parameters.cast(spellEnv, target); + parameters.cast(spellEnv, {dest}); } break; } From a04d8b34f38abbe0489bfe77507e7f326620b370 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Wed, 1 Mar 2023 16:38:54 +0300 Subject: [PATCH 09/34] vcmi: fix rmg header levels It was printed as char, not as int. --- lib/rmg/CMapGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 4f13b446d..a823b7a6e 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -160,7 +160,7 @@ std::string CMapGenerator::getMapDescription() const std::stringstream ss; ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") + ", levels %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() % - randomSeed % map->map().width % map->map().height % map->map().levels() % static_cast(mapGenOptions.getPlayerCount()) % + randomSeed % map->map().width % map->map().height % static_cast(map->map().levels()) % static_cast(mapGenOptions.getPlayerCount()) % static_cast(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] % monsterStrengthStr[monsterStrengthIndex]); From 57409a0d94338b4d1e29458f9722c2f902d5cc99 Mon Sep 17 00:00:00 2001 From: Adriankhl Date: Thu, 6 Apr 2023 18:32:21 +0200 Subject: [PATCH 10/34] fix: close dropbox with left mouse click outside of the dropbox --- client/lobby/RandomMapTab.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/client/lobby/RandomMapTab.cpp b/client/lobby/RandomMapTab.cpp index 5387c5467..339e838df 100644 --- a/client/lobby/RandomMapTab.cpp +++ b/client/lobby/RandomMapTab.cpp @@ -394,6 +394,10 @@ void TemplatesDropBox::ListItem::clickLeft(tribool down, bool previousState) { dropBox.setTemplate(item); } + else + { + dropBox.clickLeft(true, true); + } } @@ -449,8 +453,14 @@ void TemplatesDropBox::clickLeft(tribool down, bool previousState) { if(down && !hovered) { - assert(GH.topInt().get() == this); - GH.popInt(GH.topInt()); + auto w = widget("slider"); + + // pop the interface only if the mouse is not clicking on the slider + if (!w || !w->mouseState(MouseButton::LEFT)) + { + assert(GH.topInt().get() == this); + GH.popInt(GH.topInt()); + } } } From f8c15f3a4b499297ece447436d11c2558c7a8992 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 7 Apr 2023 20:50:47 +0300 Subject: [PATCH 11/34] InfoBar: disable scrollbar --- client/adventureMap/CInfoBar.cpp | 2 +- client/adventureMap/CInfoBar.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index d1c1b1b6e..d5ceb969d 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -214,7 +214,7 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector(message, textRect, 0, font, ETextAlignment::CENTER, Colors::WHITE); + text = std::make_shared(textRect, font, ETextAlignment::CENTER, Colors::WHITE, message); } void CInfoBar::playNewDaySound() diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 3a507e879..63ec62991 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -27,7 +27,7 @@ class CComponentBox; class CHeroTooltip; class CTownTooltip; class CLabel; -class CTextBox; +class CMultiLineLabel; /// Info box which shows next week/day information, hold the current date class CInfoBar : public CIntObject @@ -112,7 +112,7 @@ private: class VisibleComponentInfo : public CVisibleInfo { std::shared_ptr comps; - std::shared_ptr text; + std::shared_ptr text; public: struct Cache { From f1c52f501ddb2384b47cccbf430b5e5ba092c1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Fri, 7 Apr 2023 21:23:34 +0200 Subject: [PATCH 12/34] Fix Pandora Box creature amounts to match OH3 exactly. --- lib/rmg/TreasurePlacer.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/rmg/TreasurePlacer.cpp b/lib/rmg/TreasurePlacer.cpp index ab4efc7b7..6d16825ba 100644 --- a/lib/rmg/TreasurePlacer.cpp +++ b/lib/rmg/TreasurePlacer.cpp @@ -254,19 +254,23 @@ void TreasurePlacer::addAllPossibleObjects() if(!creature->AIValue || tierValues.empty()) //bug #2681 return 0; //this box won't be generated + //Follow the rules from https://heroes.thelazy.net/index.php/Pandora%27s_Box + int actualTier = creature->level > tierValues.size() ? tierValues.size() - 1 : creature->level - 1; - float creaturesAmount = (static_cast(tierValues[actualTier])) / creature->AIValue; - if(creaturesAmount <= 5) + float creaturesAmount = std::floor((static_cast(tierValues[actualTier])) / creature->AIValue); + if (creaturesAmount < 1) { - creaturesAmount = boost::math::round(creaturesAmount); //allow single monsters - if(creaturesAmount < 1) - return 0; + return 0; + } + else if(creaturesAmount <= 5) + { + //No change } else if(creaturesAmount <= 12) { - (creaturesAmount /= 2) *= 2; + creaturesAmount = std::ceil(creaturesAmount / 2) * 2; } else if(creaturesAmount <= 50) { @@ -556,6 +560,7 @@ std::vector TreasurePlacer::prepareTreasurePile(const CTreasureInfo //remove from possible objects assert(oi->maxPerZone); oi->maxPerZone--; + //FIXME: restore the counter if teh object is not placed successfuly? currentValue += oi->value; } From cf1f9c456b2225d1cb8a86e0c5726d3803257d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Fri, 7 Apr 2023 21:40:47 +0200 Subject: [PATCH 13/34] Removed unrelated comment --- lib/rmg/TreasurePlacer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rmg/TreasurePlacer.cpp b/lib/rmg/TreasurePlacer.cpp index 6d16825ba..14e10c805 100644 --- a/lib/rmg/TreasurePlacer.cpp +++ b/lib/rmg/TreasurePlacer.cpp @@ -560,7 +560,6 @@ std::vector TreasurePlacer::prepareTreasurePile(const CTreasureInfo //remove from possible objects assert(oi->maxPerZone); oi->maxPerZone--; - //FIXME: restore the counter if teh object is not placed successfuly? currentValue += oi->value; } From 50da08013086ff7bf3e92e3fec0b360319a6596b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 8 Apr 2023 00:13:38 +0300 Subject: [PATCH 14/34] Fixed UI of objects that provide one reward that can be refused --- lib/mapObjects/CRewardableObject.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 1d3138582..0c1086e4d 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -152,8 +152,14 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const BlockingDialog sd(canRefuse, rewards.size() > 1); sd.player = h->tempOwner; sd.text = dialog; - for (auto index : rewards) - sd.components.push_back(info[index].reward.getDisplayedComponent(h)); + + if (rewards.size() > 1) + for (auto index : rewards) + sd.components.push_back(info[index].reward.getDisplayedComponent(h)); + + if (rewards.size() == 1) + info[rewards[0]].reward.loadComponents(sd.components, h); + cb->showBlockingDialog(&sd); }; From 50c9a571335cf2616b5986fb278981816a7dbde8 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Sat, 8 Apr 2023 01:44:08 +0300 Subject: [PATCH 15/34] InfoBar: fix tiny selection with data --- client/adventureMap/CInfoBar.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index d5ceb969d..d5249d096 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -173,7 +173,7 @@ CInfoBar::VisibleComponentInfo::VisibleComponentInfo(const std::vector(vect, imageRect, 4, 4, 1, maxComponents); } - else - font = tiny ? FONT_TINY : font; if(!message.empty()) text = std::make_shared(textRect, font, ETextAlignment::CENTER, Colors::WHITE, message); From 3dedc963f7d6ebd1b23c70aebf5c307d2b0e47d4 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 8 Apr 2023 18:53:28 +0300 Subject: [PATCH 16/34] Fix double-playing of battle effects --- client/CPlayerInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 91e6516fe..5c4c0aa7a 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -941,7 +941,7 @@ void CPlayerInterface::battleStacksEffectsSet( const SetStackEffect & sse ) void CPlayerInterface::battleTriggerEffect (const BattleTriggerEffect & bte) { EVENT_HANDLER_CALLED_BY_CLIENT; - //TODO why is this different (no return on LOPLINT != this) ? + BATTLE_EVENT_POSSIBLE_RETURN; RETURN_IF_QUICK_COMBAT; battleInt->effectsController->battleTriggerEffect(bte); From 0abc00f82e99e1f7ef3512cfcaf4df9efddab727 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 8 Apr 2023 18:53:47 +0300 Subject: [PATCH 17/34] Fix assertion failure in in-game console --- client/adventureMap/CInGameConsole.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index ed4588ea4..3af427e9f 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -38,6 +38,9 @@ void CInGameConsole::showAll(SDL_Surface * to) void CInGameConsole::show(SDL_Surface * to) { + if (LOCPLINT->cingconsole != this) + return; + int number = 0; boost::unique_lock lock(texts_mx); @@ -107,7 +110,11 @@ void CInGameConsole::print(const std::string & txt) void CInGameConsole::keyPressed (const SDL_Keycode & key) { - if(!captureAllKeys && key != SDLK_TAB) return; //because user is not entering any text + if (LOCPLINT->cingconsole != this) + return; + + if(!captureAllKeys && key != SDLK_TAB) + return; //because user is not entering any text switch(key) { @@ -192,6 +199,9 @@ void CInGameConsole::keyPressed (const SDL_Keycode & key) void CInGameConsole::textInputed(const std::string & inputtedText) { + if (LOCPLINT->cingconsole != this) + return; + if(!captureAllKeys || enteredText.empty()) return; enteredText.resize(enteredText.size()-1); From 71ddaeea6ef9042731d01ad06167cc8761027a96 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 8 Apr 2023 19:47:16 +0300 Subject: [PATCH 18/34] Fix crash on missing music file --- client/CMusicHandler.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index f15ad9c3f..1649339c4 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -446,15 +446,7 @@ void CMusicHandler::queueNext(std::unique_ptr queued) void CMusicHandler::queueNext(CMusicHandler *owner, const std::string & setName, const std::string & musicURI, bool looped, bool fromStart) { - try - { - queueNext(std::make_unique(owner, setName, musicURI, looped, fromStart)); - } - catch(std::exception &e) - { - logGlobal->error("Failed to queue music. setName=%s\tmusicURI=%s", setName, musicURI); - logGlobal->error("Exception: %s", e.what()); - } + queueNext(std::make_unique(owner, setName, musicURI, looped, fromStart)); } void CMusicHandler::stopMusic(int fade_ms) @@ -563,12 +555,20 @@ void MusicEntry::load(std::string musicURI) } currentName = musicURI; + music = nullptr; logGlobal->trace("Loading music file %s", musicURI); - auto musicFile = MakeSDLRWops(CResourceHandler::get()->load(ResourceID(std::move(musicURI), EResType::MUSIC))); - - music = Mix_LoadMUS_RW(musicFile, SDL_TRUE); + try + { + auto musicFile = MakeSDLRWops(CResourceHandler::get()->load(ResourceID(std::move(musicURI), EResType::MUSIC))); + music = Mix_LoadMUS_RW(musicFile, SDL_TRUE); + } + catch(std::exception &e) + { + logGlobal->error("Failed to load music. setName=%s\tmusicURI=%s", setName, musicURI); + logGlobal->error("Exception: %s", e.what()); + } if(!music) { From 454168897e2fe85051483101ab88dbebd664df15 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 8 Apr 2023 19:47:38 +0300 Subject: [PATCH 19/34] Fix handling of invalid hotkeys in configurable UI --- client/gui/InterfaceObjectConfigurable.cpp | 14 +++++++++++--- config/widgets/settings/settingsMainContainer.json | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 79b7de24c..340c0e21b 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -31,6 +31,8 @@ static std::map KeycodeMap{ {"left", SDLK_LEFT}, {"right", SDLK_RIGHT}, {"space", SDLK_SPACE}, + {"escape", SDLK_ESCAPE}, + {"backspace", SDLK_BACKSPACE}, {"enter", SDLK_RETURN} }; @@ -220,10 +222,16 @@ int InterfaceObjectConfigurable::readKeycode(const JsonNode & config) const auto s = config.String(); if(s.size() == 1) //keyboard symbol return s[0]; - return KeycodeMap[s]; + + if (KeycodeMap.count(s)) + return KeycodeMap[s]; + + logGlobal->error("Invalid keycode '%s' in interface configuration!", config.String()); + return SDLK_UNKNOWN; } - - return 0; + + logGlobal->error("Invalid keycode format in interface configuration! Expected string or integer!", config.String()); + return SDLK_UNKNOWN; } std::shared_ptr InterfaceObjectConfigurable::buildPicture(const JsonNode & config) const diff --git a/config/widgets/settings/settingsMainContainer.json b/config/widgets/settings/settingsMainContainer.json index 8e7d78f8f..014fc1d96 100644 --- a/config/widgets/settings/settingsMainContainer.json +++ b/config/widgets/settings/settingsMainContainer.json @@ -153,7 +153,7 @@ "imageOrder": [1, 0, 2, 3], "help": "core.help.325", "callback": "closeWindow", - "hotkey": ["esc", "backspace"] + "hotkey": ["escape", "backspace"] } ] } From 84af64ce6b66ee8697e923b56a190304937c0b60 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 8 Apr 2023 21:29:04 +0300 Subject: [PATCH 20/34] Removed magical initialization of registry via static variables --- client/LobbyClientNetPackVisitors.h | 4 +++- lib/spells/effects/Catapult.cpp | 4 ---- lib/spells/effects/Clone.cpp | 4 ---- lib/spells/effects/Damage.cpp | 4 ---- lib/spells/effects/DemonSummon.cpp | 4 ---- lib/spells/effects/Dispel.cpp | 4 ---- lib/spells/effects/Effect.h | 3 --- lib/spells/effects/Heal.cpp | 5 ---- lib/spells/effects/Obstacle.cpp | 4 ---- lib/spells/effects/Registry.cpp | 33 +++++++++++++++++++++++++++ lib/spells/effects/Registry.h | 18 --------------- lib/spells/effects/RemoveObstacle.cpp | 4 ---- lib/spells/effects/Sacrifice.cpp | 5 ---- lib/spells/effects/Summon.cpp | 5 ---- lib/spells/effects/Teleport.cpp | 6 ----- lib/spells/effects/Timed.cpp | 4 ---- 16 files changed, 36 insertions(+), 75 deletions(-) diff --git a/client/LobbyClientNetPackVisitors.h b/client/LobbyClientNetPackVisitors.h index 9290c8f0e..7ea105b02 100644 --- a/client/LobbyClientNetPackVisitors.h +++ b/client/LobbyClientNetPackVisitors.h @@ -12,7 +12,9 @@ #include "../lib/NetPackVisitor.h" class CClient; +VCMI_LIB_NAMESPACE_BEGIN class CGameState; +VCMI_LIB_NAMESPACE_END class ApplyOnLobbyHandlerNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor) { @@ -53,4 +55,4 @@ public: virtual void visitLobbyStartGame(LobbyStartGame & pack) override; virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override; virtual void visitLobbyShowMessage(LobbyShowMessage & pack) override; -}; \ No newline at end of file +}; diff --git a/lib/spells/effects/Catapult.cpp b/lib/spells/effects/Catapult.cpp index 2d0c8d24d..e6a3c0782 100644 --- a/lib/spells/effects/Catapult.cpp +++ b/lib/spells/effects/Catapult.cpp @@ -23,15 +23,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:catapult"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Catapult, EFFECT_NAME); - bool Catapult::applicable(Problem & problem, const Mechanics * m) const { const auto *town = m->battle()->battleGetDefendedTown(); diff --git a/lib/spells/effects/Clone.cpp b/lib/spells/effects/Clone.cpp index 7dbe418e5..dadfb4d04 100644 --- a/lib/spells/effects/Clone.cpp +++ b/lib/spells/effects/Clone.cpp @@ -19,15 +19,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:clone"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Clone, EFFECT_NAME); - void Clone::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { for(const Destination & dest : target) diff --git a/lib/spells/effects/Damage.cpp b/lib/spells/effects/Damage.cpp index 0879d16bb..ccc727ec7 100644 --- a/lib/spells/effects/Damage.cpp +++ b/lib/spells/effects/Damage.cpp @@ -22,15 +22,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:damage"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Damage, EFFECT_NAME); - void Damage::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { StacksInjured stacksInjured; diff --git a/lib/spells/effects/DemonSummon.cpp b/lib/spells/effects/DemonSummon.cpp index 274b4be70..40ec769cc 100644 --- a/lib/spells/effects/DemonSummon.cpp +++ b/lib/spells/effects/DemonSummon.cpp @@ -19,15 +19,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:demonSummon"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(DemonSummon, EFFECT_NAME); - void DemonSummon::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { BattleUnitsChanged pack; diff --git a/lib/spells/effects/Dispel.cpp b/lib/spells/effects/Dispel.cpp index 94836900f..88a437d12 100644 --- a/lib/spells/effects/Dispel.cpp +++ b/lib/spells/effects/Dispel.cpp @@ -23,15 +23,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:dispel"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Dispel, EFFECT_NAME); - void Dispel::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { const bool describe = server->describeChanges(); diff --git a/lib/spells/effects/Effect.h b/lib/spells/effects/Effect.h index 8e35945df..d70b6ec7a 100644 --- a/lib/spells/effects/Effect.h +++ b/lib/spells/effects/Effect.h @@ -35,9 +35,6 @@ class Effects; class Effect; class Registry; -template -class RegisterEffect; - using TargetType = spells::AimType; class DLL_LINKAGE Effect diff --git a/lib/spells/effects/Heal.cpp b/lib/spells/effects/Heal.cpp index 1354e8b0d..894b3f592 100644 --- a/lib/spells/effects/Heal.cpp +++ b/lib/spells/effects/Heal.cpp @@ -22,16 +22,11 @@ VCMI_LIB_NAMESPACE_BEGIN - -static const std::string EFFECT_NAME = "core:heal"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Heal, EFFECT_NAME); - void Heal::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { apply(m->getEffectValue(), server, m, target); diff --git a/lib/spells/effects/Obstacle.cpp b/lib/spells/effects/Obstacle.cpp index 36b60d908..1a8c7b7a2 100644 --- a/lib/spells/effects/Obstacle.cpp +++ b/lib/spells/effects/Obstacle.cpp @@ -21,15 +21,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:obstacle"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Obstacle, EFFECT_NAME); - using RelativeShape = std::vector>; static void serializeRelativeShape(JsonSerializeFormat & handler, const std::string & fieldName, RelativeShape & value) diff --git a/lib/spells/effects/Registry.cpp b/lib/spells/effects/Registry.cpp index 0377c55c7..82e3b1c48 100644 --- a/lib/spells/effects/Registry.cpp +++ b/lib/spells/effects/Registry.cpp @@ -11,6 +11,23 @@ #include "Registry.h" +#include "Catapult.h" +#include "Clone.h" +#include "Damage.h" +#include "DemonSummon.h" +#include "Dispel.h" +#include "Effect.h" +#include "Effects.h" +#include "Heal.h" +#include "LocationEffect.h" +#include "Obstacle.h" +#include "RemoveObstacle.h" +#include "Sacrifice.h" +#include "Summon.h" +#include "Teleport.h" +#include "Timed.h" +#include "UnitEffect.h" + VCMI_LIB_NAMESPACE_BEGIN namespace spells @@ -23,6 +40,22 @@ namespace detail class RegistryImpl : public Registry { public: + RegistryImpl() + { + add("core:catapult", std::make_shared>()); + add("core:clone", std::make_shared>()); + add("core:damage", std::make_shared>()); + add("core:demonSummon", std::make_shared>()); + add("core:dispel", std::make_shared>()); + add("core:heal", std::make_shared>()); + add("core:obstacle", std::make_shared>()); + add("core:removeObstacle", std::make_shared>()); + add("core:sacrifice", std::make_shared>()); + add("core:summon", std::make_shared>()); + add("core:teleport", std::make_shared>()); + add("core:timed", std::make_shared>()); + } + const IEffectFactory * find(const std::string & name) const override { auto iter = data.find(name); diff --git a/lib/spells/effects/Registry.h b/lib/spells/effects/Registry.h index 912cced73..dc56eeb4b 100644 --- a/lib/spells/effects/Registry.h +++ b/lib/spells/effects/Registry.h @@ -12,13 +12,6 @@ #include "Effect.h" -#define VCMI_REGISTER_SPELL_EFFECT(Type, Name) \ -namespace\ -{\ -RegisterEffect register ## Type(Name);\ -}\ -\ - VCMI_LIB_NAMESPACE_BEGIN namespace spells @@ -60,17 +53,6 @@ public: } }; -template -class RegisterEffect -{ -public: - RegisterEffect(const std::string & name) - { - auto f = std::make_shared>(); - GlobalRegistry::get()->add(name, f); - } -}; - } } diff --git a/lib/spells/effects/RemoveObstacle.cpp b/lib/spells/effects/RemoveObstacle.cpp index c268fd8c8..68732ea8d 100644 --- a/lib/spells/effects/RemoveObstacle.cpp +++ b/lib/spells/effects/RemoveObstacle.cpp @@ -22,15 +22,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:removeObstacle"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(RemoveObstacle, EFFECT_NAME); - bool RemoveObstacle::applicable(Problem & problem, const Mechanics * m) const { if (getTargets(m, EffectTarget(), true).empty()) diff --git a/lib/spells/effects/Sacrifice.cpp b/lib/spells/effects/Sacrifice.cpp index 4ca9ca73a..704b93576 100644 --- a/lib/spells/effects/Sacrifice.cpp +++ b/lib/spells/effects/Sacrifice.cpp @@ -21,16 +21,11 @@ VCMI_LIB_NAMESPACE_BEGIN - -static const std::string EFFECT_NAME = "core:sacrifice"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Sacrifice, EFFECT_NAME); - void Sacrifice::adjustTargetTypes(std::vector & types) const { if(!types.empty()) diff --git a/lib/spells/effects/Summon.cpp b/lib/spells/effects/Summon.cpp index 0b18fcc0e..18e700ff0 100644 --- a/lib/spells/effects/Summon.cpp +++ b/lib/spells/effects/Summon.cpp @@ -24,16 +24,11 @@ VCMI_LIB_NAMESPACE_BEGIN - -static const std::string EFFECT_NAME = "core:summon"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Summon, EFFECT_NAME); - void Summon::adjustAffectedHexes(std::set & hexes, const Mechanics * m, const Target & spellTarget) const { //no hexes affected diff --git a/lib/spells/effects/Teleport.cpp b/lib/spells/effects/Teleport.cpp index eb4a5e9e0..af7607460 100644 --- a/lib/spells/effects/Teleport.cpp +++ b/lib/spells/effects/Teleport.cpp @@ -18,16 +18,10 @@ VCMI_LIB_NAMESPACE_BEGIN -//TODO: Teleport effect - -static const std::string EFFECT_NAME = "core:teleport"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Teleport, EFFECT_NAME); - void Teleport::adjustTargetTypes(std::vector & types) const { diff --git a/lib/spells/effects/Timed.cpp b/lib/spells/effects/Timed.cpp index 83a795315..b00f4a2dd 100644 --- a/lib/spells/effects/Timed.cpp +++ b/lib/spells/effects/Timed.cpp @@ -20,15 +20,11 @@ VCMI_LIB_NAMESPACE_BEGIN -static const std::string EFFECT_NAME = "core:timed"; - namespace spells { namespace effects { -VCMI_REGISTER_SPELL_EFFECT(Timed, EFFECT_NAME); - static void describeEffect(std::vector & log, const Mechanics * m, const std::vector & bonuses, const battle::Unit * target) { auto addLogLine = [&](const int32_t baseTextID, const boost::logic::tribool & plural) From 1d6192ca62b83902bda9357114051b114b827472 Mon Sep 17 00:00:00 2001 From: Adriankhl Date: Tue, 4 Apr 2023 21:54:30 +0200 Subject: [PATCH 21/34] Shut down the thread for tactic phase properly --- client/CPlayerInterface.cpp | 6 ++++++ client/CPlayerInterface.h | 1 + client/Client.cpp | 25 ++++++++++++++++++++++++- client/Client.h | 5 +++++ lib/CGameInterface.h | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 91e6516fe..fc8c58560 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1032,6 +1032,12 @@ void CPlayerInterface::yourTacticPhase(int distance) boost::this_thread::sleep(boost::posix_time::millisec(1)); } +void CPlayerInterface::forceEndTacticPhase() +{ + if (battleInt) + battleInt->tacticsMode = false; +} + void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &text, const std::vector & components, int soundID) { EVENT_HANDLER_CALLED_BY_CLIENT; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 591ecf260..5d8be29c4 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -226,6 +226,7 @@ public: void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack void battleGateStateChanged(const EGateState state) override; void yourTacticPhase(int distance) override; + void forceEndTacticPhase() override; //-------------// void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList onYes); diff --git a/client/Client.cpp b/client/Client.cpp index d153722af..686395719 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -7,6 +7,7 @@ * Full text of license available in license.txt file, in main folder * */ +#include "Global.h" #include "StdInc.h" #include "Client.h" @@ -31,6 +32,7 @@ #include "../lib/registerTypes/RegisterTypes.h" #include "../lib/serializer/Connection.h" +#include #include #if SCRIPTING_ENABLED @@ -369,6 +371,9 @@ void CClient::endGame() logNetwork->info("Deleted mapHandler and gameState."); } + //threads cleanup has to be after gs cleanup and before battleints cleanup to stop tacticThread + cleanThreads(); + playerint.clear(); battleints.clear(); battleCallbacks.clear(); @@ -593,7 +598,8 @@ void CClient::battleStarted(const BattleInfo * info) if(info->tacticDistance && vstd::contains(battleints, info->sides[info->tacticsSide].color)) { - boost::thread(&CClient::commenceTacticPhaseForInt, this, battleints[info->sides[info->tacticsSide].color]); + PlayerColor color = info->sides[info->tacticsSide].color; + playerTacticThreads[color] = std::make_unique(&CClient::commenceTacticPhaseForInt, this, battleints[color]); } } @@ -754,6 +760,23 @@ void CClient::removeGUI() LOCPLINT = nullptr; } +void CClient::cleanThreads() +{ + stopAllBattleActions(); + + while (!playerTacticThreads.empty()) + { + PlayerColor color = playerTacticThreads.begin()->first; + + //set tacticcMode of the players to false to stop tacticThread + if (vstd::contains(battleints, color)) + battleints[color]->forceEndTacticPhase(); + + playerTacticThreads[color]->join(); + playerTacticThreads.erase(color); + } +} + #ifdef VCMI_ANDROID #ifndef SINGLE_PROCESS_APP extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jclass cls) diff --git a/client/Client.h b/client/Client.h index 140b5ad0f..cb84e901b 100644 --- a/client/Client.h +++ b/client/Client.h @@ -9,6 +9,7 @@ */ #pragma once +#include #include #include "../lib/IGameCallback.h" @@ -241,6 +242,8 @@ public: void showInfoDialog(const std::string & msg, PlayerColor player) override {}; void removeGUI(); + void cleanThreads(); + #if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; scripting::Pool * getContextPool() const override; @@ -262,6 +265,8 @@ private: std::map> playerActionThreads; + std::map> playerTacticThreads; + void waitForMoveAndSend(PlayerColor color); void reinitScripting(); }; diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index fee41e281..4b93c7b04 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -82,6 +82,7 @@ public: //battle call-ins virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack virtual void yourTacticPhase(int distance){}; //called when interface has opportunity to use Tactics skill -> use cb->battleMakeTacticAction from this function + virtual void forceEndTacticPhase(){}; //force the tatic phase to end to clean up the tactic phase thread }; /// Central class for managing human player / AI interface logic From a6ce99573c3170755981b995fa3eea5d0d806638 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 9 Apr 2023 12:27:33 +0300 Subject: [PATCH 22/34] Added fallback identifiers for reading older vcmi maps --- config/objects/rewardableBonusing.json | 37 +++++++++++++-------- config/objects/rewardableOncePerHero.json | 22 ++++++++---- config/objects/rewardableOncePerWeek.json | 15 ++++++--- config/objects/rewardableOnceVisitable.json | 14 +++++--- config/objects/rewardablePickable.json | 13 +++++--- lib/mapObjects/CObjectClassesHandler.cpp | 4 +++ 6 files changed, 72 insertions(+), 33 deletions(-) diff --git a/config/objects/rewardableBonusing.json b/config/objects/rewardableBonusing.json index a616f7a1f..ff1f4c2cd 100644 --- a/config/objects/rewardableBonusing.json +++ b/config/objects/rewardableBonusing.json @@ -18,7 +18,8 @@ "value" : 100, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "blockedVisitable" : true, "onVisitedMessage" : 22, "visitMode" : "bonus", @@ -49,7 +50,8 @@ "value" : 100, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 30, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -81,7 +83,8 @@ "value" : 100, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 50, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -112,7 +115,8 @@ "value" : 100, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 56, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -164,7 +168,8 @@ "value" : 100, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 58, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -195,7 +200,8 @@ "value" : 100, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 63, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -250,7 +256,8 @@ "value" : 100, "rarity" : 20 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 82, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -280,7 +287,8 @@ "value" : 100, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 95, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -312,10 +320,10 @@ "value" : 200, "rarity" : 40 }, - + "compatibilityIdentifiers" : [ "object" ], + "visitMode" : "bonus", "selectMode" : "selectFirst", - "onVisited" : [ { "message" : 139, @@ -370,7 +378,8 @@ "value" : 100, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 141, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -406,7 +415,8 @@ "value" : 100, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 111, "visitMode" : "bonus", "selectMode" : "selectFirst", @@ -440,7 +450,8 @@ "value" : 500, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 167, "visitMode" : "bonus", "selectMode" : "selectFirst", diff --git a/config/objects/rewardableOncePerHero.json b/config/objects/rewardableOncePerHero.json index d052866bb..e6bac178a 100644 --- a/config/objects/rewardableOncePerHero.json +++ b/config/objects/rewardableOncePerHero.json @@ -18,7 +18,8 @@ "value" : 3000, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onSelectMessage" : 0, "onVisitedMessage" : 1, "visitMode" : "hero", @@ -51,6 +52,7 @@ "value" : 1500, "rarity" : 100 }, + "compatibilityIdentifiers" : [ "object" ], "onVisitedMessage" : 40, "visitMode" : "hero", @@ -81,7 +83,8 @@ "value" : 1500, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 60, "visitMode" : "hero", "selectMode" : "selectFirst", @@ -110,7 +113,8 @@ "value" : 12000, "rarity" : 20 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 67, "onEmptyMessage" : 68, "visitMode" : "hero", @@ -154,7 +158,8 @@ "value" : 1500, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 81, "visitMode" : "hero", "selectMode" : "selectFirst", @@ -184,7 +189,8 @@ "value" : 1500, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 101, "visitMode" : "hero", "selectMode" : "selectFirst", @@ -214,6 +220,7 @@ "value" : 2500, "rarity" : 50 }, + "compatibilityIdentifiers" : [ "object" ], "onEmpty" : [ { @@ -270,7 +277,8 @@ "value" : 1000, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onSelectMessage" : 71, "onVisitedMessage" : 72, "onEmptyMessage" : 73, @@ -309,6 +317,7 @@ "value" : 1000, "rarity" : 50 }, + "compatibilityIdentifiers" : [ "object" ], "onSelectMessage" : 158, "onVisitedMessage" : 159, @@ -348,6 +357,7 @@ "value" : 1500, "rarity" : 200 }, + "compatibilityIdentifiers" : [ "object" ], "onVisitedMessage" : 144, "visitMode" : "hero", diff --git a/config/objects/rewardableOncePerWeek.json b/config/objects/rewardableOncePerWeek.json index 5288a7ef3..464f8ff32 100644 --- a/config/objects/rewardableOncePerWeek.json +++ b/config/objects/rewardableOncePerWeek.json @@ -17,7 +17,8 @@ "value" : 250, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onEmptyMessage" : 79, "onVisitedMessage" : 78, "visitMode" : "bonus", @@ -54,7 +55,8 @@ // "value" : 500, // "rarity" : 50 //}, - + "compatibilityIdentifiers" : [ "object" ], + "onEmptyMessage" : 76, "onVisitedMessage" : 75, "resetParameters" : { @@ -92,7 +94,8 @@ "value" : 500, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 93, "resetParameters" : { "period" : 7, @@ -133,7 +136,8 @@ "value" : 1500, "rarity" : 80 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 169, "resetParameters" : { "period" : 7, @@ -174,7 +178,8 @@ "value" : 750, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 165, "resetParameters" : { "period" : 7, diff --git a/config/objects/rewardableOnceVisitable.json b/config/objects/rewardableOnceVisitable.json index ea504f2b0..3eab395fc 100644 --- a/config/objects/rewardableOnceVisitable.json +++ b/config/objects/rewardableOnceVisitable.json @@ -17,7 +17,8 @@ "value" : 500, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 65, "visitMode" : "once", "selectMode" : "selectFirst", @@ -32,7 +33,7 @@ } ] } - ] + ] } } }, @@ -52,7 +53,8 @@ "value" : 500, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 38, "blockedVisitable" : true, "visitMode" : "once", @@ -92,7 +94,8 @@ "value" : 500, "rarity" : 50 }, - + "compatibilityIdentifiers" : [ "object" ], + "onVisitedMessage" : 156, "visitMode" : "once", "selectMode" : "selectFirst", @@ -142,7 +145,8 @@ "value" : 6000, "rarity" : 20 }, - + "compatibilityIdentifiers" : [ "object" ], + "onSelectMessage" : 161, "visitMode" : "once", "selectMode" : "selectFirst", diff --git a/config/objects/rewardablePickable.json b/config/objects/rewardablePickable.json index 868c9239c..2f4b64ef9 100644 --- a/config/objects/rewardablePickable.json +++ b/config/objects/rewardablePickable.json @@ -19,7 +19,8 @@ "value" : 2000, "rarity" : 500 }, - + "compatibilityIdentifiers" : [ "object" ], + "blockedVisitable" : true, "visitMode" : "unlimited", "selectMode" : "selectFirst", @@ -60,7 +61,8 @@ "value" : 2000, "rarity" : 100 }, - + "compatibilityIdentifiers" : [ "object" ], + "blockedVisitable" : true, "visitMode" : "unlimited", "selectMode" : "selectFirst", @@ -117,7 +119,8 @@ "value" : 1500, "rarity" : 500 }, - + "compatibilityIdentifiers" : [ "object" ], + "blockedVisitable" : true, "visitMode" : "unlimited", "selectMode" : "selectFirst", @@ -167,6 +170,7 @@ "value" : 1500, "rarity" : 50 }, + "compatibilityIdentifiers" : [ "object" ], "blockedVisitable" : true, "visitMode" : "unlimited", @@ -217,7 +221,8 @@ "value" : 1500, "rarity" : 1000 }, - + "compatibilityIdentifiers" : [ "object" ], + "blockedVisitable" : true, "onSelectMessage" : 146, "visitMode" : "unlimited", diff --git a/lib/mapObjects/CObjectClassesHandler.cpp b/lib/mapObjects/CObjectClassesHandler.cpp index 8a9d50256..3b142bb75 100644 --- a/lib/mapObjects/CObjectClassesHandler.cpp +++ b/lib/mapObjects/CObjectClassesHandler.cpp @@ -165,6 +165,8 @@ void CObjectClassesHandler::loadSubObject(const std::string & scope, const std:: obj->objects.push_back(object); registerObject(scope, obj->getJsonKey(), object->getSubTypeName(), object->subtype); + for (auto const & compatID : entry["compatibilityIdentifiers"].Vector()) + registerObject(scope, obj->getJsonKey(), compatID.String(), object->subtype); } void CObjectClassesHandler::loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index) @@ -176,6 +178,8 @@ void CObjectClassesHandler::loadSubObject(const std::string & scope, const std:: obj->objects[index] = object; registerObject(scope, obj->getJsonKey(), object->getSubTypeName(), object->subtype); + for (auto const & compatID : entry["compatibilityIdentifiers"].Vector()) + registerObject(scope, obj->getJsonKey(), compatID.String(), object->subtype); } TObjectTypeHandler CObjectClassesHandler::loadSubObjectFromJson(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index) From 737c34b8c6afc8ae8223ae9f75e8dd707a17e8a3 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 8 Apr 2023 14:05:47 +0300 Subject: [PATCH 23/34] BattleAI: avoid selfblocking on siege --- AI/BattleAI/AttackPossibility.cpp | 4 ++- AI/BattleAI/BattleAI.cpp | 45 ++++++++++++++++++++------ AI/BattleAI/BattleAI.h | 1 + AI/BattleAI/BattleExchangeVariant.cpp | 7 ++++ AI/BattleAI/BattleExchangeVariant.h | 2 +- lib/battle/BattleStateInfoForRetreat.h | 1 + 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index cfda077f3..8098e4668 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -212,11 +212,13 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInf // check how much damage we gain from blocking enemy shooters on this hex bestAp.shootersBlockedDmg = evaluateBlockedShootersDmg(attackInfo, hex, state); - logAi->debug("BattleAI best AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld", +#if BATTLE_TRACE_LEVEL>=1 + logAi->trace("BattleAI best AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld", attackInfo.attacker->unitType()->getJsonKey(), attackInfo.defender->unitType()->getJsonKey(), (int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(), bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg); +#endif //TODO other damage related to attack (eg. fire shield and other abilities) return bestAp; diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 99a5c922e..8cf523b62 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -89,6 +89,7 @@ void CBattleAI::initBattleInterface(std::shared_ptr ENV, std::share wasUnlockingGs = CB->unlockGsWhenWaiting; CB->waitTillRealize = true; CB->unlockGsWhenWaiting = false; + movesSkippedByDefense = 0; } BattleAction CBattleAI::activeStack( const CStack * stack ) @@ -181,6 +182,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) if(bestSpellcast.is_initialized() && bestSpellcast->value > bestAttack.damageDiff()) { // return because spellcast value is damage dealt and score is dps reduce + movesSkippedByDefense = 0; return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id); } @@ -196,14 +198,15 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) } else if(bestAttack.attack.shooting) { - result = BattleAction::makeShotAttack(stack, bestAttack.attack.defender); action = "shot"; + movesSkippedByDefense = 0; } else { result = BattleAction::makeMeleeAttack(stack, bestAttack.attack.defender->getPosition(), bestAttack.from); action = "melee"; + movesSkippedByDefense = 0; } logAi->debug("BattleAI: %s -> %s x %d, %s, from %d curpos %d dist %d speed %d: +%lld -%lld = %lld", @@ -217,6 +220,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) } else if(bestSpellcast.is_initialized()) { + movesSkippedByDefense = 0; return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id); } @@ -235,12 +239,8 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) } } - if(score > EvaluationResult::INEFFECTIVE_SCORE) - { - return result; - } - - if(!stack->hasBonusOfType(Bonus::FLYING) + if(score <= EvaluationResult::INEFFECTIVE_SCORE + && !stack->hasBonusOfType(Bonus::FLYING) && stack->unitSide() == BattleSide::ATTACKER && cb->battleGetSiegeLevel() >= CGTownInstance::CITADEL) { @@ -248,10 +248,12 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) if(brokenWallMoat.size()) { + movesSkippedByDefense = 0; + if(stack->doubleWide() && vstd::contains(brokenWallMoat, stack->getPosition())) - return BattleAction::makeMove(stack, stack->getPosition().cloneInDirection(BattleHex::RIGHT)); + result = BattleAction::makeMove(stack, stack->getPosition().cloneInDirection(BattleHex::RIGHT)); else - return goTowardsNearest(stack, brokenWallMoat); + result = goTowardsNearest(stack, brokenWallMoat); } } } @@ -264,6 +266,15 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what()); } + if(result.actionType == EActionType::DEFEND) + { + movesSkippedByDefense++; + } + else if(result.actionType != EActionType::WAIT) + { + movesSkippedByDefense = 0; + } + return result; } @@ -285,7 +296,9 @@ BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vectorcoversPos(hex)) { @@ -396,6 +409,8 @@ BattleAction CBattleAI::useCatapult(const CStack * stack) attack.side = side; attack.stackNumber = stack->ID; + movesSkippedByDefense = 0; + return attack; } @@ -703,6 +718,7 @@ void CBattleAI::attemptCastingSpell() spellcast.side = side; spellcast.stackNumber = (!side) ? -1 : -2; cb->battleMakeAction(&spellcast); + movesSkippedByDefense = 0; } else { @@ -796,12 +812,21 @@ boost::optional CBattleAI::considerFleeingOrSurrendering() } } + bs.turnsSkippedByDefense = movesSkippedByDefense / bs.ourStacks.size(); + if(!bs.canFlee || !bs.canSurrender) { return boost::none; } - return cb->makeSurrenderRetreatDecision(bs); + auto result = cb->makeSurrenderRetreatDecision(bs); + + if(!result && bs.canFlee && bs.turnsSkippedByDefense > 30) + { + return BattleAction::makeRetreat(bs.ourSide); + } + + return result; } diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index 679d54f0a..dd9499d51 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -60,6 +60,7 @@ class CBattleAI : public CBattleGameInterface //Previous setting of cb bool wasWaitingForRealize, wasUnlockingGs; + int movesSkippedByDefense; public: CBattleAI(); diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index cbc4a2d7f..a0100e4a8 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -355,6 +355,13 @@ int64_t BattleExchangeEvaluator::calculateExchange( logAi->trace("Battle exchange at %lld", ap.attack.shooting ? ap.dest : ap.from); #endif + if(cb->battleGetMySide() == BattlePerspective::LEFT_SIDE + && cb->battleGetGateState() == EGateState::BLOCKED + && ap.attack.defender->coversPos(ESiegeHex::GATE_BRIDGE)) + { + return EvaluationResult::INEFFECTIVE_SCORE; + } + std::vector ourStacks; std::vector enemyStacks; diff --git a/AI/BattleAI/BattleExchangeVariant.h b/AI/BattleAI/BattleExchangeVariant.h index d6082c2b5..6c8e0b448 100644 --- a/AI/BattleAI/BattleExchangeVariant.h +++ b/AI/BattleAI/BattleExchangeVariant.h @@ -42,7 +42,7 @@ struct EvaluationResult bool defend; EvaluationResult(const AttackPossibility & ap) - :wait(false), score(0), bestAttack(ap), defend(false) + :wait(false), score(INEFFECTIVE_SCORE), bestAttack(ap), defend(false) { } }; diff --git a/lib/battle/BattleStateInfoForRetreat.h b/lib/battle/BattleStateInfoForRetreat.h index 4dc713e41..341ed7ffe 100644 --- a/lib/battle/BattleStateInfoForRetreat.h +++ b/lib/battle/BattleStateInfoForRetreat.h @@ -29,6 +29,7 @@ public: std::vector enemyStacks; const CGHeroInstance * ourHero; const CGHeroInstance * enemyHero; + int turnsSkippedByDefense; BattleStateInfoForRetreat(); uint64_t getOurStrength() const; From 02c9a3f3d8ad537e0f598c3abc35202ecc136e0a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 10 Apr 2023 14:33:24 +0300 Subject: [PATCH 24/34] Correctly reset UI on aborting Clone/Sacrifice --- client/battle/BattleActionsController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index 18c31acb4..05421de07 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -128,6 +128,7 @@ void BattleActionsController::endCastingSpell() if(owner.stacksController->getActiveStack()) possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack()); //restore actions after they were cleared + selectedStack = nullptr; GH.fakeMouseMove(); } From 5046f916d69c0081f38c32a5c247267c93aa4646 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 10 Apr 2023 14:33:53 +0300 Subject: [PATCH 25/34] Fix filtering of objects by terrain in editor --- mapeditor/mainwindow.cpp | 14 +++++++++----- mapeditor/mainwindow.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index ce3150731..71af287bf 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -553,7 +553,10 @@ void MainWindow::loadObjectsTree() connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain->getId()); }); //filter - ui->terrainFilterCombo->addItem(QString::fromStdString(terrain->getNameTranslated())); + QString displayName = QString::fromStdString(terrain->getNameTranslated()); + QString uniqueName = QString::fromStdString(terrain->getJsonKey()); + + ui->terrainFilterCombo->addItem(displayName, QVariant(uniqueName)); } //add spacer to keep terrain button on the top ui->terrainLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); @@ -929,23 +932,24 @@ void MainWindow::treeViewSelected(const QModelIndex & index, const QModelIndex & preparePreview(index); } -void MainWindow::on_terrainFilterCombo_currentTextChanged(const QString &arg1) +void MainWindow::on_terrainFilterCombo_currentIndexChanged(int index) { if(!objectBrowser) return; + QString uniqueName = ui->terrainFilterCombo->itemData(index).toString(); + objectBrowser->terrain = TerrainId(ETerrainId::ANY_TERRAIN); - if (!arg1.isEmpty()) + if (!uniqueName.isEmpty()) { for (auto const & terrain : VLC->terrainTypeHandler->objects) - if (terrain->getJsonKey() == arg1.toStdString()) + if (terrain->getJsonKey() == uniqueName.toStdString()) objectBrowser->terrain = terrain->getId(); } objectBrowser->invalidate(); objectBrowser->sort(0); } - void MainWindow::on_filter_textChanged(const QString &arg1) { if(!objectBrowser) diff --git a/mapeditor/mainwindow.h b/mapeditor/mainwindow.h index 2408a4dd5..16f5e6a18 100644 --- a/mapeditor/mainwindow.h +++ b/mapeditor/mainwindow.h @@ -83,7 +83,7 @@ private slots: void on_toolErase_clicked(); - void on_terrainFilterCombo_currentTextChanged(const QString &arg1); + void on_terrainFilterCombo_currentIndexChanged(int index); void on_filter_textChanged(const QString &arg1); From 07230f3b471880dc06e2213c3da440d8b58b578c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 10 Apr 2023 14:34:17 +0300 Subject: [PATCH 26/34] Attempt to fix stack splitting on macos --- client/gui/CGuiHandler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 3135104fe..831107021 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -712,7 +712,11 @@ void CGuiHandler::moveCursorToPosition(const Point & position) bool CGuiHandler::isKeyboardCtrlDown() const { +#ifdef VCMI_MAC + return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LGUI] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RGUI]; +#else return SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_RCTRL]; +#endif } bool CGuiHandler::isKeyboardAltDown() const From 40bea35a263447350067e2b4cfe61f48810f9a3d Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 10 Apr 2023 15:01:29 +0300 Subject: [PATCH 27/34] CHeroWindow: fix picked art mana limit display --- client/windows/CHeroWindow.cpp | 3 ++- client/windows/CHeroWindow.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index 234d07285..74def291b 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -63,7 +63,8 @@ int64_t CHeroWithMaybePickedArtifact::getTreeVersion() const si32 CHeroWithMaybePickedArtifact::manaLimit() const { - return hero->manaLimit(); + //TODO: reduplicate code with CGHeroInstance + return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE) * (valOfBonuses(Bonus::MANA_PER_KNOWLEDGE))); } CHeroWithMaybePickedArtifact::CHeroWithMaybePickedArtifact(CWindowWithArtifacts * Cww, const CGHeroInstance * Hero) diff --git a/client/windows/CHeroWindow.h b/client/windows/CHeroWindow.h index 6a8bf2dbe..263b50009 100644 --- a/client/windows/CHeroWindow.h +++ b/client/windows/CHeroWindow.h @@ -56,7 +56,7 @@ public: int64_t getTreeVersion() const override; - si32 manaLimit() const override; + si32 manaLimit() const; }; class CHeroWindow : public CStatusbarWindow, public CGarrisonHolder, public CWindowWithArtifacts From 4f3c826196545837eb61b60395a1003efe30af8b Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 10 Apr 2023 14:40:08 +0300 Subject: [PATCH 28/34] vcmi: manaLimit should be spellcaster property --- include/vcmi/spells/Caster.h | 2 ++ lib/HeroBonus.cpp | 5 ----- lib/HeroBonus.h | 1 - lib/battle/CUnitState.cpp | 5 +++++ lib/battle/CUnitState.h | 1 + lib/spells/ProxyCaster.cpp | 8 ++++++++ lib/spells/ProxyCaster.h | 1 + 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/vcmi/spells/Caster.h b/include/vcmi/spells/Caster.h index 123865a97..acd48cc6e 100644 --- a/include/vcmi/spells/Caster.h +++ b/include/vcmi/spells/Caster.h @@ -65,6 +65,8 @@ public: virtual void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const = 0; virtual void spendMana(ServerCallback * server, const int32_t spellCost) const = 0; + + virtual int32_t manaLimit() const = 0; }; } diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 109b74e50..41c4e14a3 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -789,11 +789,6 @@ int IBonusBearer::getMaxDamage(bool ranged) const return valOfBonuses(selector, cachingStr); } -si32 IBonusBearer::manaLimit() const -{ - return 0; -} - int IBonusBearer::getPrimSkillLevel(PrimarySkill::PrimarySkill id) const { static const CSelector selectorAllSkills = Selector::type()(Bonus::PRIMARY_SKILL); diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 195d4bc93..690f26b2a 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -752,7 +752,6 @@ public: virtual si32 magicResistance() const; ui32 Speed(int turn = 0, bool useBind = false) const; //get speed of creature with all modificators - virtual si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge) int getPrimSkillLevel(PrimarySkill::PrimarySkill id) const; virtual int64_t getTreeVersion() const = 0; diff --git a/lib/battle/CUnitState.cpp b/lib/battle/CUnitState.cpp index fdb17a179..90b748b25 100644 --- a/lib/battle/CUnitState.cpp +++ b/lib/battle/CUnitState.cpp @@ -478,6 +478,11 @@ void CUnitState::getCastDescription(const spells::Spell * spell, const std::vect text.addReplacement(MetaString::SPELL_NAME, spell->getIndex()); } +int32_t CUnitState::manaLimit() const +{ + return 0; //TODO: creature casting with mana mode (for mods) +} + bool CUnitState::ableToRetaliate() const { return alive() diff --git a/lib/battle/CUnitState.h b/lib/battle/CUnitState.h index ee130edb6..a31789fd8 100644 --- a/lib/battle/CUnitState.h +++ b/lib/battle/CUnitState.h @@ -194,6 +194,7 @@ public: PlayerColor getCasterOwner() const override; void getCasterName(MetaString & text) const override; void getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const override; + int32_t manaLimit() const override; bool ableToRetaliate() const override; bool alive() const override; diff --git a/lib/spells/ProxyCaster.cpp b/lib/spells/ProxyCaster.cpp index e93afe58c..9243975b5 100644 --- a/lib/spells/ProxyCaster.cpp +++ b/lib/spells/ProxyCaster.cpp @@ -86,6 +86,14 @@ void ProxyCaster::spendMana(ServerCallback * server, const int32_t spellCost) co actualCaster->spendMana(server, spellCost); } +int32_t ProxyCaster::manaLimit() const +{ + if(actualCaster) + return actualCaster->manaLimit(); + + return 0; +} + } VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/ProxyCaster.h b/lib/spells/ProxyCaster.h index b3829da7f..9a2455c75 100644 --- a/lib/spells/ProxyCaster.h +++ b/lib/spells/ProxyCaster.h @@ -35,6 +35,7 @@ public: void getCasterName(MetaString & text) const override; void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override; void spendMana(ServerCallback * server, const int32_t spellCost) const override; + int32_t manaLimit() const override; private: const Caster * actualCaster; From 2eaecfd1ae3d964363e9c3697c0c1a189ca15ce4 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 10 Apr 2023 15:41:58 +0300 Subject: [PATCH 29/34] ObstacleCasterProxy: beta branch --- server/CGameHandler.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4ed3dad0f..cf0ed56d5 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -168,6 +168,11 @@ public: return owner; } + int32_t manaLimit() const override + { + return 0; + } + void getCasterName(MetaString & text) const override { logGlobal->error("Unexpected call to ObstacleCasterProxy::getCasterName"); From 94d339ca22d4f12b4fdfd0921cc1a17f29ec8ad8 Mon Sep 17 00:00:00 2001 From: Michael <13953785+Laserlicht@users.noreply.github.com> Date: Mon, 10 Apr 2023 17:24:15 +0200 Subject: [PATCH 30/34] Android Launcher - Language (#1914) * codepage -> language * Update strings.xml * change mod repo on android --- .../java/eu/vcmi/vcmi/ActivityLauncher.java | 10 ++-- .../main/java/eu/vcmi/vcmi/ActivityMods.java | 2 +- .../src/main/java/eu/vcmi/vcmi/Config.java | 16 +++---- .../vcmi/settings/CodepageSettingDialog.java | 40 ---------------- ...er.java => LanguageSettingController.java} | 16 +++---- .../vcmi/settings/LanguageSettingDialog.java | 47 +++++++++++++++++++ .../src/main/res/values-de/strings.xml | 6 +-- .../src/main/res/values-pl/strings.xml | 6 +-- .../src/main/res/values-ru/strings.xml | 8 ++-- .../src/main/res/values-uk/strings.xml | 8 ++-- .../vcmi-app/src/main/res/values/strings.xml | 6 +-- 11 files changed, 86 insertions(+), 79 deletions(-) delete mode 100644 android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CodepageSettingDialog.java rename android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/{CodepageSettingController.java => LanguageSettingController.java} (53%) create mode 100644 android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java diff --git a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityLauncher.java b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityLauncher.java index d5f6a5b00..fde7580af 100644 --- a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityLauncher.java +++ b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityLauncher.java @@ -18,7 +18,7 @@ import java.util.List; import eu.vcmi.vcmi.content.AsyncLauncherInitialization; import eu.vcmi.vcmi.settings.AdventureAiController; -import eu.vcmi.vcmi.settings.CodepageSettingController; +import eu.vcmi.vcmi.settings.LanguageSettingController; import eu.vcmi.vcmi.settings.CopyDataController; import eu.vcmi.vcmi.settings.ExportDataController; import eu.vcmi.vcmi.settings.LauncherSettingController; @@ -45,7 +45,7 @@ public class ActivityLauncher extends ActivityWithToolbar private TextView mErrorMessage; private Config mConfig; private LauncherSettingController mCtrlScreenRes; - private LauncherSettingController mCtrlCodepage; + private LauncherSettingController mCtrlLanguage; private LauncherSettingController mCtrlPointerMode; private LauncherSettingController mCtrlStart; private LauncherSettingController mCtrlPointerMulti; @@ -203,7 +203,7 @@ public class ActivityLauncher extends ActivityWithToolbar (mCtrlExport = new ExportDataController(this)).init(R.id.launcher_btn_export); new ModsBtnController(this, v -> startActivity(new Intent(ActivityLauncher.this, ActivityMods.class))).init(R.id.launcher_btn_mods); mCtrlScreenRes = new ScreenResSettingController(this).init(R.id.launcher_btn_res, mConfig); - mCtrlCodepage = new CodepageSettingController(this).init(R.id.launcher_btn_cp, mConfig); + mCtrlLanguage = new LanguageSettingController(this).init(R.id.launcher_btn_cp, mConfig); mCtrlPointerMode = new PointerModeSettingController(this).init(R.id.launcher_btn_pointer_mode, mConfig); mCtrlPointerMulti = new PointerMultiplierSettingController(this).init(R.id.launcher_btn_pointer_multi, mConfig); mCtrlSoundVol = new SoundSettingController(this).init(R.id.launcher_btn_volume_sound, mConfig); @@ -211,7 +211,7 @@ public class ActivityLauncher extends ActivityWithToolbar mAiController = new AdventureAiController(this).init(R.id.launcher_btn_adventure_ai, mConfig); mActualSettings.clear(); - mActualSettings.add(mCtrlCodepage); + mActualSettings.add(mCtrlLanguage); mActualSettings.add(mCtrlScreenRes); mActualSettings.add(mCtrlPointerMode); mActualSettings.add(mCtrlPointerMulti); @@ -267,7 +267,7 @@ public class ActivityLauncher extends ActivityWithToolbar private void onConfigUpdated() { updateCtrlConfig(mCtrlScreenRes, mConfig); - updateCtrlConfig(mCtrlCodepage, mConfig); + updateCtrlConfig(mCtrlLanguage, mConfig); updateCtrlConfig(mCtrlPointerMode, mConfig); updateCtrlConfig(mCtrlPointerMulti, mConfig); updateCtrlConfig(mCtrlSoundVol, mConfig); diff --git a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java index 7b010b903..31fdd60ff 100644 --- a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java +++ b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java @@ -50,7 +50,7 @@ import eu.vcmi.vcmi.util.ServerResponse; public class ActivityMods extends ActivityWithToolbar { private static final boolean ENABLE_REPO_DOWNLOADING = true; - private static final String REPO_URL = "https://raw.githubusercontent.com/vcmi/vcmi-mods-repository/develop/github.json"; + private static final String REPO_URL = "https://raw.githubusercontent.com/vcmi/vcmi-mods-repository/develop/vcmi-1.2.json"; private VCMIModsRepo mRepo; private RecyclerView mRecycler; diff --git a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/Config.java b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/Config.java index 65bd3093b..e894c52e0 100644 --- a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/Config.java +++ b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/Config.java @@ -14,13 +14,13 @@ import eu.vcmi.vcmi.util.Log; */ public class Config { - public static final String DEFAULT_CODEPAGE = "CP1250"; + public static final String DEFAULT_LANGUAGE = "english"; public static final int DEFAULT_MUSIC_VALUE = 5; public static final int DEFAULT_SOUND_VALUE = 5; public static final int DEFAULT_SCREEN_RES_W = 800; public static final int DEFAULT_SCREEN_RES_H = 600; - public String mCodepage; + public String mLanguage; public int mResolutionWidth; public int mResolutionHeight; public boolean mSwipeEnabled; @@ -85,7 +85,7 @@ public class Config final Config config = new Config(); final JSONObject general = accessNode(obj, "general"); final JSONObject server = accessNode(obj, "server"); - config.mCodepage = loadEntry(general, "encoding", DEFAULT_CODEPAGE); + config.mLanguage = loadEntry(general, "language", DEFAULT_LANGUAGE); config.mVolumeSound = loadEntry(general, "sound", DEFAULT_SOUND_VALUE); config.mVolumeMusic = loadEntry(general, "music", DEFAULT_MUSIC_VALUE); config.mSwipeEnabled = loadEntry(general, "swipe", true); @@ -101,9 +101,9 @@ public class Config return config; } - public void updateCodepage(final String s) + public void updateLanguage(final String s) { - mCodepage = s; + mLanguage = s; mIsModified = true; } @@ -202,9 +202,9 @@ public class Config final JSONObject screenRes = screenResNode == null ? new JSONObject() : screenResNode; final JSONObject server = serverNode == null ? new JSONObject() : serverNode; - if (mCodepage != null) + if (mLanguage != null) { - general.put("encoding", mCodepage); + general.put("language", mLanguage); } general.put("swipe", mSwipeEnabled); @@ -230,4 +230,4 @@ public class Config return root.toString(); } -} \ No newline at end of file +} diff --git a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CodepageSettingDialog.java b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CodepageSettingDialog.java deleted file mode 100644 index 3656d3ba9..000000000 --- a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CodepageSettingDialog.java +++ /dev/null @@ -1,40 +0,0 @@ -package eu.vcmi.vcmi.settings; - -import java.util.ArrayList; -import java.util.List; - -import eu.vcmi.vcmi.R; - -/** - * @author F - */ -public class CodepageSettingDialog extends LauncherSettingDialog -{ - private static final List AVAILABLE_CODEPAGES = new ArrayList<>(); - - static - { - AVAILABLE_CODEPAGES.add("CP1250"); - AVAILABLE_CODEPAGES.add("CP1251"); - AVAILABLE_CODEPAGES.add("CP1252"); - AVAILABLE_CODEPAGES.add("GBK"); - AVAILABLE_CODEPAGES.add("GB2312"); - } - - public CodepageSettingDialog() - { - super(AVAILABLE_CODEPAGES); - } - - @Override - protected int dialogTitleResId() - { - return R.string.launcher_btn_cp_title; - } - - @Override - protected CharSequence itemName(final String item) - { - return item; - } -} diff --git a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CodepageSettingController.java b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingController.java similarity index 53% rename from android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CodepageSettingController.java rename to android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingController.java index 93d319df7..e4e226419 100644 --- a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CodepageSettingController.java +++ b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingController.java @@ -8,9 +8,9 @@ import eu.vcmi.vcmi.R; /** * @author F */ -public class CodepageSettingController extends LauncherSettingWithDialogController +public class LanguageSettingController extends LauncherSettingWithDialogController { - public CodepageSettingController(final AppCompatActivity activity) + public LanguageSettingController(final AppCompatActivity activity) { super(activity); } @@ -18,20 +18,20 @@ public class CodepageSettingController extends LauncherSettingWithDialogControll @Override protected LauncherSettingDialog dialog() { - return new CodepageSettingDialog(); + return new LanguageSettingDialog(); } @Override public void onItemChosen(final String item) { - mConfig.updateCodepage(item); + mConfig.updateLanguage(item); updateContent(); } @Override protected String mainText() { - return mActivity.getString(R.string.launcher_btn_cp_title); + return mActivity.getString(R.string.launcher_btn_language_title); } @Override @@ -41,8 +41,8 @@ public class CodepageSettingController extends LauncherSettingWithDialogControll { return ""; } - return mConfig.mCodepage == null || mConfig.mCodepage.isEmpty() - ? mActivity.getString(R.string.launcher_btn_cp_subtitle_unknown) - : mActivity.getString(R.string.launcher_btn_cp_subtitle, mConfig.mCodepage); + return mConfig.mLanguage == null || mConfig.mLanguage.isEmpty() + ? mActivity.getString(R.string.launcher_btn_language_subtitle_unknown) + : mActivity.getString(R.string.launcher_btn_language_subtitle, mConfig.mLanguage); } } diff --git a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java new file mode 100644 index 000000000..1fb5e8894 --- /dev/null +++ b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java @@ -0,0 +1,47 @@ +package eu.vcmi.vcmi.settings; + +import java.util.ArrayList; +import java.util.List; + +import eu.vcmi.vcmi.R; + +/** + * @author F + */ +public class LanguageSettingDialog extends LauncherSettingDialog +{ + private static final List AVAILABLE_LANGUAGES = new ArrayList<>(); + + static + { + AVAILABLE_LANGUAGES.add("english"); + AVAILABLE_LANGUAGES.add("chinese"); + AVAILABLE_LANGUAGES.add("french"); + AVAILABLE_LANGUAGES.add("german"); + AVAILABLE_LANGUAGES.add("korean"); + AVAILABLE_LANGUAGES.add("polish"); + AVAILABLE_LANGUAGES.add("russian"); + AVAILABLE_LANGUAGES.add("spanish"); + AVAILABLE_LANGUAGES.add("ukrainian"); + AVAILABLE_LANGUAGES.add("other_cp1250"); + AVAILABLE_LANGUAGES.add("other_cp1251"); + AVAILABLE_LANGUAGES.add("other_cp1252"); + } + + public LanguageSettingDialog() + { + super(AVAILABLE_LANGUAGES); + } + + @Override + protected int dialogTitleResId() + { + return R.string.launcher_btn_language_title; + } + + @Override + protected CharSequence itemName(final String item) + { + return item; + } +} diff --git a/android/vcmi-app/src/main/res/values-de/strings.xml b/android/vcmi-app/src/main/res/values-de/strings.xml index df616c931..04bc85961 100644 --- a/android/vcmi-app/src/main/res/values-de/strings.xml +++ b/android/vcmi-app/src/main/res/values-de/strings.xml @@ -10,9 +10,9 @@ Aktuelle VCMI-Version: %1$s Mods Neue Burgen, Kreaturen, Objekte und Erweiterungen hinzufügen - Zeichensatz - Aktuell: unbekannt - Aktuell: %1$s + Sprache + Aktuell: unbekannt + Aktuell: %1$s Zeigermodus ändern Aktuell: %1$s Aktuell: %1$s diff --git a/android/vcmi-app/src/main/res/values-pl/strings.xml b/android/vcmi-app/src/main/res/values-pl/strings.xml index 771fb9495..0061449d0 100644 --- a/android/vcmi-app/src/main/res/values-pl/strings.xml +++ b/android/vcmi-app/src/main/res/values-pl/strings.xml @@ -10,9 +10,9 @@ Obecna wersja VCMI: %1$s Mody Zainstaluj nowe frakcje, obiekty, dodatki - Strona kodowa - Obecnie: nieznane - Obecnie: %1$s + Język + Obecnie: nieznane + Obecnie: %1$s Zmień tryb kursora Obecnie: %1$s Mnożnik prędkości kursora diff --git a/android/vcmi-app/src/main/res/values-ru/strings.xml b/android/vcmi-app/src/main/res/values-ru/strings.xml index b78cd4f05..7caa88ebb 100644 --- a/android/vcmi-app/src/main/res/values-ru/strings.xml +++ b/android/vcmi-app/src/main/res/values-ru/strings.xml @@ -10,9 +10,9 @@ Текущая версия VCMI: %1$s Моды Добавить новые замки, существа, объекты, расширения - Кодовая страница - Текущая: неизвестно - Текущая: %1$s + Язык + Текущая: неизвестно + Текущая: %1$s Изменить режим управления указателем Currently: %1$s Текущая: %1$s @@ -60,4 +60,4 @@ Загрузить данные VCMI во внутреннее хранилище Скопировать данные VCMI во внутреннее хранилище. Вы можете загрузить старую папку vcmi-data от версии 0.99 или файлы Героев Копируем %1$s - \ No newline at end of file + diff --git a/android/vcmi-app/src/main/res/values-uk/strings.xml b/android/vcmi-app/src/main/res/values-uk/strings.xml index 10ed5c286..095441f32 100644 --- a/android/vcmi-app/src/main/res/values-uk/strings.xml +++ b/android/vcmi-app/src/main/res/values-uk/strings.xml @@ -10,9 +10,9 @@ Поточна версія VCMI: %1$s Моди Додати нові замки, істот, об’єкти, розширення - Кодова сторінка - Поточна: невідомо - Поточна: %1$s + Мова + Поточна: невідомо + Поточна: %1$s Змінити режим керування курсором Поточна: %1$s Поточна: %1$s @@ -60,4 +60,4 @@ Завантажити дані VCMI у внутрішнє сховище Копіювати дані VCMI у внутрішнє сховище. Ви можете завантажити стару папку vcmi-data від версії 0.99 чи файли героїв Копіюємо %1$s - \ No newline at end of file + diff --git a/android/vcmi-app/src/main/res/values/strings.xml b/android/vcmi-app/src/main/res/values/strings.xml index 8858a876e..563911f18 100644 --- a/android/vcmi-app/src/main/res/values/strings.xml +++ b/android/vcmi-app/src/main/res/values/strings.xml @@ -15,9 +15,9 @@ Current VCMI version: %1$s Mods Install new factions, objects, extras - Codepage - Currently: unknown - Currently: %1$s + Language + Currently: unknown + Currently: %1$s Change pointer mode Currently: %1$s Relative pointer speed multiplier From 49255e5a3f3d9e9c3a69231df8a380cac4636bd4 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 10 Apr 2023 20:33:02 +0400 Subject: [PATCH 31/34] Adjustable layout for launcher --- launcher/modManager/cmodlistview_moc.ui | 321 ++++++++++++------------ 1 file changed, 159 insertions(+), 162 deletions(-) diff --git a/launcher/modManager/cmodlistview_moc.ui b/launcher/modManager/cmodlistview_moc.ui index 4b8060ed7..72961f923 100644 --- a/launcher/modManager/cmodlistview_moc.ui +++ b/launcher/modManager/cmodlistview_moc.ui @@ -14,21 +14,6 @@ - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - @@ -124,154 +109,166 @@ - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - - 24 - 24 - - - - QAbstractItemView::ScrollPerItem - - - QAbstractItemView::ScrollPerPixel - - - true - - - false - - - - - - - 0 - - - - Description - - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 0 - 0 - - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> + + + + 0 + 0 + + + + Qt::Horizontal + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + 24 + 24 + + + + QAbstractItemView::ScrollPerItem + + + QAbstractItemView::ScrollPerPixel + + + true + + + false + + + + + + 0 + 0 + + + + 0 + + + + Description + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 0 + + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +hr { height: 1px; border-width: 0; } +</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;"><br /></p></body></html> - - - true - - - true - - - - - - - - Changelog - - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - - - - Screenshots - - - - 4 - - - 4 - - - 4 - - - 4 - - - - - Qt::ScrollBarAlwaysOff - - - QAbstractItemView::NoSelection - - - QAbstractItemView::SelectRows - - - - 240 - 180 - - - - QListView::IconMode - - - true - - - - - + + + true + + + true + + + + - - + + + Changelog + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + + + + Screenshots + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + + + + 240 + 180 + + + + QListView::IconMode + + + true + + + + + + + @@ -279,7 +276,7 @@ p, li { white-space: pre-wrap; } true - + 0 0 @@ -306,7 +303,7 @@ p, li { white-space: pre-wrap; } - + 0 0 From 3c87b3934dbcde406b711e7f446e5f3e3a780630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Mon, 10 Apr 2023 19:26:53 +0200 Subject: [PATCH 32/34] Do not place object visible tiles over the top of the map. --- lib/mapObjects/CObjectHandler.cpp | 5 +++++ lib/mapObjects/CObjectHandler.h | 1 + lib/mapObjects/ObjectTemplate.cpp | 17 +++++++++++++++++ lib/mapObjects/ObjectTemplate.h | 9 ++++++++- lib/rmg/ObjectManager.cpp | 6 ++++++ lib/rmg/RmgObject.cpp | 18 ++++++++++++++++++ lib/rmg/RmgObject.h | 2 ++ 7 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index f7658209e..a2169e341 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -133,6 +133,11 @@ int3 CGObjectInstance::getPosition() const return pos; } +int3 CGObjectInstance::getTopVisiblePos() const +{ + return pos - appearance->getTopVisibleOffset(); +} + void CGObjectInstance::setOwner(const PlayerColor & ow) { tempOwner = ow; diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index f79fb3d79..cdf7f6136 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -159,6 +159,7 @@ public: bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) (h3m pos) int3 visitablePos() const override; int3 getPosition() const override; + int3 getTopVisiblePos() const; bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) (h3m pos) bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) (h3m pos) std::set getBlockedPos() const; //returns set of positions blocked by this object diff --git a/lib/mapObjects/ObjectTemplate.cpp b/lib/mapObjects/ObjectTemplate.cpp index ad022cdb0..cf36e66a6 100644 --- a/lib/mapObjects/ObjectTemplate.cpp +++ b/lib/mapObjects/ObjectTemplate.cpp @@ -524,6 +524,22 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const return dirMap[dy][dx] != 0; } +void ObjectTemplate::calculateTopVisibleOffset() +{ + for(int y = static_cast(getHeight()) - 1; y >= 0; y--) //Templates start from bottom-right corner + { + for(int x = 0; x < static_cast(getWidth()); x++) + { + if (isVisibleAt(x, y)) + { + topVisibleOffset = int3(x, y, 0); + return; + } + } + } + topVisibleOffset = int3(0, 0, 0); +} + void ObjectTemplate::calculateVisitableOffset() { for(int y = 0; y < static_cast(getHeight()); y++) @@ -559,6 +575,7 @@ void ObjectTemplate::recalculate() calculateBlockedOffsets(); calculateBlockMapOffset(); calculateVisitableOffset(); + calculateTopVisibleOffset(); if (visitable && visitDir == 0) logMod->warn("Template for %s is visitable but has no visitable directions!", animationFile); diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index 64e2e46ea..dcc9f7b90 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -87,7 +87,12 @@ public: inline int3 getBlockMapOffset() const { return blockMapOffset; - }; + }; + + inline int3 getTopVisibleOffset() const + { + return topVisibleOffset; + } // Checks if object is visitable from certain direction. X and Y must be between -1..+1 bool isVisitableFrom(si8 X, si8 Y) const; @@ -137,6 +142,7 @@ private: std::set blockedOffsets; int3 blockMapOffset; int3 visitableOffset; + int3 topVisibleOffset; void recalculate(); @@ -146,6 +152,7 @@ private: void calculateBlockedOffsets(); void calculateBlockMapOffset(); void calculateVisitableOffset(); + void calculateTopVisibleOffset(); public: template void serialize(Handler &h, const int version) diff --git a/lib/rmg/ObjectManager.cpp b/lib/rmg/ObjectManager.cpp index 5143706d4..4f2b293b6 100644 --- a/lib/rmg/ObjectManager.cpp +++ b/lib/rmg/ObjectManager.cpp @@ -112,6 +112,9 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object continue; obj.setPosition(tile); + + if (obj.getVisibleTop().y < 0) + continue; if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea())) continue; @@ -131,6 +134,9 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object for(const auto & tile : searchArea.getTiles()) { obj.setPosition(tile); + + if (obj.getVisibleTop().y < 0) + continue; if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea())) continue; diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 3b76a00f1..a073e9614 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -45,6 +45,11 @@ const Area & Object::Instance::getBlockedArea() const return dBlockedAreaCache; } +int3 Object::Instance::getTopTile() const +{ + return object().getTopVisiblePos(); +} + int3 Object::Instance::getPosition(bool isAbsolute) const { if(isAbsolute) @@ -284,6 +289,19 @@ const Area & Object::getArea() const return dFullAreaCache; } +const int3 Object::getVisibleTop() const +{ + int3 topTile(-1, 10000, -1); //Start at the bottom + for (const auto& i : dInstances) + { + if (i.getTopTile().y < topTile.y) + { + topTile = i.getTopTile(); + } + } + return topTile; +} + void Object::Instance::finalize(RmgMap & map) { if(!map.isOnMap(getPosition(true))) diff --git a/lib/rmg/RmgObject.h b/lib/rmg/RmgObject.h index 24a6f51b2..a164b9161 100644 --- a/lib/rmg/RmgObject.h +++ b/lib/rmg/RmgObject.h @@ -38,6 +38,7 @@ public: void setTemplate(TerrainId terrain); //cache invalidation void setAnyTemplate(); //cache invalidation + int3 getTopTile() const; int3 getPosition(bool isAbsolute = false) const; void setPosition(const int3 & position); //cache invalidation void setPositionRaw(const int3 & position); //no cache invalidation @@ -75,6 +76,7 @@ public: void setTemplate(const TerrainId & terrain); const Area & getArea() const; //lazy cache invalidation + const int3 getVisibleTop() const; void finalize(RmgMap & map); void clear(); From 4bd3cc16793e43272c8373c4c56ce5cd6765fa75 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Mon, 10 Apr 2023 20:45:41 +0200 Subject: [PATCH 33/34] Update lib/mapObjects/ObjectTemplate.h Co-authored-by: Nordsoft91 --- lib/mapObjects/ObjectTemplate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index dcc9f7b90..ef48b0b57 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -87,7 +87,7 @@ public: inline int3 getBlockMapOffset() const { return blockMapOffset; - }; + } inline int3 getTopVisibleOffset() const { From b82374c4a9fb75c492e1ba58d550fd10d5b14ca5 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 10 Apr 2023 19:49:44 +0300 Subject: [PATCH 34/34] config: Orb of Vulnerability should block all resistances Just set MAGIC_RESISTANCE and SPELL_RESISTANCE_AURA to 0 by INDEPENDENT_MIN, will work. --- config/artifacts.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/config/artifacts.json b/config/artifacts.json index eb90dc52e..3290fe817 100644 --- a/config/artifacts.json +++ b/config/artifacts.json @@ -1377,6 +1377,18 @@ "subtype" : 1, "val" : 0, "valueType" : "BASE_NUMBER" + }, + { + "type" : "MAGIC_RESISTANCE", + "val" : 0, + "valueType" : "INDEPENDENT_MIN", + "propagator": "BATTLE_WIDE" + }, + { + "type" : "SPELL_RESISTANCE_AURA", + "val" : 0, + "valueType" : "INDEPENDENT_MIN", + "propagator": "BATTLE_WIDE" } ], "index" : 93,