From 3f7e5fcfc344408b97fb0fb029d64ab17acc977d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 2 Aug 2023 21:23:27 +0300 Subject: [PATCH 01/36] Fix possible assertion failure on resolution change --- client/adventureMap/AdventureMapInterface.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 0f7ef98e2..45ea69a63 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -822,6 +822,15 @@ void AdventureMapInterface::hotkeyZoom(int delta) void AdventureMapInterface::onScreenResize() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; + + // remember our activation state and reactive after reconstruction + // since othervice activate() calls for created elements will bypass virtual dispatch + // and will call directly CIntObject::activate() instead of dispatching virtual function call + bool widgetActive = isActive(); + + if (widgetActive) + deactivate(); + widget.reset(); pos.x = pos.y = 0; pos.w = GH.screenDimensions().x; @@ -838,4 +847,7 @@ void AdventureMapInterface::onScreenResize() widget->getMapView()->onCenteredObject(LOCPLINT->localState->getCurrentArmy()); adjustActiveness(); + + if (widgetActive) + activate(); } From 3213800097817e83951cff7bd6c13bcd44e11f9e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 3 Aug 2023 14:20:54 +0300 Subject: [PATCH 02/36] Fix assertion failure on teleport cast --- client/battle/BattleInterface.cpp | 2 ++ client/battle/BattleStacksController.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/client/battle/BattleInterface.cpp b/client/battle/BattleInterface.cpp index 1f49a635c..6420cdc77 100644 --- a/client/battle/BattleInterface.cpp +++ b/client/battle/BattleInterface.cpp @@ -445,6 +445,8 @@ void BattleInterface::spellCast(const BattleSpellCast * sc) stacksController->addNewAnim(new EffectAnimation(*this, side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero)); }); } + + // animations will be executed by spell effects } void BattleInterface::battleStacksEffectsSet(const SetStackEffect & sse) diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 4321d3dac..4abc8b6ed 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -499,7 +499,7 @@ void BattleStacksController::stacksAreAttacked(std::vector at void BattleStacksController::stackTeleported(const CStack *stack, std::vector destHex, int distance) { assert(destHex.size() > 0); - owner.checkForAnimations(); + //owner.checkForAnimations(); // NOTE: at this point spellcast animations were added, but not executed owner.addToAnimationStage(EAnimationEvents::HIT, [=](){ addNewAnim( new ColorTransformAnimation(owner, stack, "teleportFadeOut", nullptr) ); From 57d7dc97bf6474d2c3066781a42d1639ee6d728f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 3 Aug 2023 14:21:15 +0300 Subject: [PATCH 03/36] Fix assertion failure on swiping during spellcast --- client/gui/CursorHandler.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/client/gui/CursorHandler.h b/client/gui/CursorHandler.h index 4ef4fe8a5..531594b3a 100644 --- a/client/gui/CursorHandler.h +++ b/client/gui/CursorHandler.h @@ -31,8 +31,6 @@ namespace Cursor }; enum class Combat { - INVALID = -1, - BLOCKED = 0, MOVE = 1, FLY = 2, @@ -157,12 +155,16 @@ public: template Index get() { - assert((std::is_same::value )|| type != Cursor::Type::DEFAULT ); - assert((std::is_same::value )|| type != Cursor::Type::ADVENTURE ); - assert((std::is_same::value )|| type != Cursor::Type::COMBAT ); - assert((std::is_same::value )|| type != Cursor::Type::SPELLBOOK ); + bool typeValid = true; - return static_cast(frame); + typeValid &= (std::is_same::value )|| type != Cursor::Type::DEFAULT; + typeValid &= (std::is_same::value )|| type != Cursor::Type::ADVENTURE; + typeValid &= (std::is_same::value )|| type != Cursor::Type::COMBAT; + typeValid &= (std::is_same::value )|| type != Cursor::Type::SPELLBOOK; + + if (typeValid) + return static_cast(frame); + return Index::POINTER; } Point getPivotOffsetDefault(size_t index); From 4fdc022cdf6f95da81540f879addd69f83ccb1da Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 3 Aug 2023 23:48:55 +0300 Subject: [PATCH 04/36] Version bump to 1.3.1 --- ChangeLog.md | 3 +++ android/vcmi-app/build.gradle | 4 ++-- cmake_modules/VersionDefinition.cmake | 2 +- debian/changelog | 6 ++++++ launcher/eu.vcmi.VCMI.metainfo.xml | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 24edce517..677e55f26 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,6 @@ +# 1.3.0 -> 1.3.1 +(unreleased) + # 1.2.1 -> 1.3.0 ### GENERAL: diff --git a/android/vcmi-app/build.gradle b/android/vcmi-app/build.gradle index 1d818d14a..4f4fc1133 100644 --- a/android/vcmi-app/build.gradle +++ b/android/vcmi-app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "is.xyz.vcmi" minSdk 19 targetSdk 31 - versionCode 1302 - versionName "1.3.0" + versionCode 1310 + versionName "1.3.1" setProperty("archivesBaseName", "vcmi") } diff --git a/cmake_modules/VersionDefinition.cmake b/cmake_modules/VersionDefinition.cmake index 5b8409584..e90590d32 100644 --- a/cmake_modules/VersionDefinition.cmake +++ b/cmake_modules/VersionDefinition.cmake @@ -1,6 +1,6 @@ set(VCMI_VERSION_MAJOR 1) set(VCMI_VERSION_MINOR 3) -set(VCMI_VERSION_PATCH 0) +set(VCMI_VERSION_PATCH 1) add_definitions( -DVCMI_VERSION_MAJOR=${VCMI_VERSION_MAJOR} -DVCMI_VERSION_MINOR=${VCMI_VERSION_MINOR} diff --git a/debian/changelog b/debian/changelog index 25cc2af65..f7e3380cc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +vcmi (1.3.1) jammy; urgency=medium + + * New upstream release + + -- Ivan Savenko Fri, 18 Aug 2023 16:00:00 +0200 + vcmi (1.3.0) jammy; urgency=medium * New upstream release diff --git a/launcher/eu.vcmi.VCMI.metainfo.xml b/launcher/eu.vcmi.VCMI.metainfo.xml index aac246f10..f89354b39 100644 --- a/launcher/eu.vcmi.VCMI.metainfo.xml +++ b/launcher/eu.vcmi.VCMI.metainfo.xml @@ -51,6 +51,7 @@ StrategyGame + From d459b3b52dc6488be79f4fddbfa102c9a94ddc38 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 5 Aug 2023 23:49:15 +0300 Subject: [PATCH 05/36] Updated changelog --- ChangeLog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 677e55f26..3ba074a01 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,11 @@ # 1.3.0 -> 1.3.1 (unreleased) +* Fixed crash on starting game with outdated mods +* Fixed Android mod manager crash +* Fixed framerate drops on hero movement with active hota mod +* Reverted FPS limit on mobile systems back to 60 fps + # 1.2.1 -> 1.3.0 ### GENERAL: From a4297ebdf618510d089b34822278322464ae33f2 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sun, 6 Aug 2023 08:57:14 +0300 Subject: [PATCH 06/36] NKAI: fix potential concurrency and town treat calculation --- .../Analyzers/DangerHitMapAnalyzer.cpp | 62 +++++++++++-------- .../Analyzers/DangerHitMapAnalyzer.h | 15 ++++- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp index efc4bde8e..3651e567f 100644 --- a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp @@ -53,6 +53,13 @@ void DangerHitMapAnalyzer::updateHitMap() } } + auto ourTowns = cb->getTownsInfo(); + + for(auto town : ourTowns) + { + townTreats[town->id]; // insert empty list + } + foreach_tile_pos([&](const int3 & pos){ hitMap[pos.x][pos.y][pos.z].reset(); }); @@ -95,33 +102,33 @@ void DangerHitMapAnalyzer::updateHitMap() node.fastestDanger = newTreat; } - if(newTreat.turn == 0) + auto objects = cb->getVisitableObjs(pos, false); + + for(auto obj : objects) { - auto objects = cb->getVisitableObjs(pos, false); - - for(auto obj : objects) + if(obj->ID == Obj::TOWN && obj->getOwner() == ai->playerID) { - if(cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) - enemyHeroAccessibleObjects[path.targetHero].insert(obj); + auto & treats = townTreats[obj->id]; + auto treat = std::find_if(treats.begin(), treats.end(), [&](const HitMapInfo & i) -> bool + { + return i.hero.hid == path.targetHero->id; + }); - if(obj->ID == Obj::TOWN && obj->getOwner() == ai->playerID) + if(treat == treats.end()) { - auto & treats = townTreats[obj->id]; - auto treat = std::find_if(treats.begin(), treats.end(), [&](const HitMapInfo & i) -> bool - { - return i.hero.hid == path.targetHero->id; - }); + treats.emplace_back(); + treat = std::prev(treats.end(), 1); + } - if(treat == treats.end()) - { - treats.emplace_back(); - treat = std::prev(treats.end(), 1); - } + if(newTreat.value() > treat->value()) + { + *treat = newTreat; + } - if(newTreat.value() > treat->value()) - { - *treat = newTreat; - } + if(newTreat.turn == 0) + { + if(cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) + enemyHeroAccessibleObjects.emplace_back(path.targetHero, obj); } } } @@ -274,16 +281,17 @@ const HitMapNode & DangerHitMapAnalyzer::getTileTreat(const int3 & tile) const const std::set empty = {}; -const std::set & DangerHitMapAnalyzer::getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const +std::set DangerHitMapAnalyzer::getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const { - auto result = enemyHeroAccessibleObjects.find(enemy); - - if(result == enemyHeroAccessibleObjects.end()) + std::set result; + + for(auto & obj : enemyHeroAccessibleObjects) { - return empty; + if(obj.hero == enemy) + result.insert(obj.obj); } - return result->second; + return result; } void DangerHitMapAnalyzer::reset() diff --git a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h index 79c56c2c4..614312649 100644 --- a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h +++ b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h @@ -55,11 +55,22 @@ struct HitMapNode } }; +struct EnemyHeroAccessibleObject +{ + const CGHeroInstance * hero; + const CGObjectInstance * obj; + + EnemyHeroAccessibleObject(const CGHeroInstance * hero, const CGObjectInstance * obj) + :hero(hero), obj(obj) + { + } +}; + class DangerHitMapAnalyzer { private: boost::multi_array hitMap; - std::map> enemyHeroAccessibleObjects; + tbb::concurrent_vector enemyHeroAccessibleObjects; bool hitMapUpToDate = false; bool tileOwnersUpToDate = false; const Nullkiller * ai; @@ -73,7 +84,7 @@ public: uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const; const HitMapNode & getObjectTreat(const CGObjectInstance * obj) const; const HitMapNode & getTileTreat(const int3 & tile) const; - const std::set & getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const; + std::set getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const; void reset(); void resetTileOwners() { tileOwnersUpToDate = false; } PlayerColor getTileOwner(const int3 & tile) const; From 60f0a4553addd11445d4f840e8132e19fa5a9471 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 5 Aug 2023 13:49:49 +0300 Subject: [PATCH 07/36] NKAI: fix freeze on army gathering --- .../Behaviors/GatherArmyBehavior.cpp | 28 +++++++++---------- AI/Nullkiller/Engine/Nullkiller.cpp | 5 ++++ AI/VCAI/VCAI.cpp | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp b/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp index f5eb28c79..c73b374c0 100644 --- a/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp +++ b/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp @@ -92,15 +92,6 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her continue; } - bool garrisoned = false; - - if(path.turn() == 0 && hero->inTownGarrison) - { -#if NKAI_TRACE_LEVEL >= 1 - garrisoned = true; -#endif - } - if(path.turn() > 0 && ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) { #if NKAI_TRACE_LEVEL >= 2 @@ -184,15 +175,22 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her composition.addNext(heroExchange); - if(garrisoned && path.turn() == 0) + if(hero->inTownGarrison && path.turn() == 0) { auto lockReason = ai->nullkiller->getHeroLockedReason(hero); - composition.addNextSequence({ - sptr(ExchangeSwapTownHeroes(hero->visitedTown)), - sptr(exchangePath), - sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason)) - }); + if(path.targetHero->visitedTown == hero->visitedTown) + { + composition.addNextSequence({ + sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))}); + } + else + { + composition.addNextSequence({ + sptr(ExchangeSwapTownHeroes(hero->visitedTown)), + sptr(exchangePath), + sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))}); + } } else { diff --git a/AI/Nullkiller/Engine/Nullkiller.cpp b/AI/Nullkiller/Engine/Nullkiller.cpp index 66b28ca8e..d6d7f41dc 100644 --- a/AI/Nullkiller/Engine/Nullkiller.cpp +++ b/AI/Nullkiller/Engine/Nullkiller.cpp @@ -323,6 +323,11 @@ void Nullkiller::makeTurn() } executeTask(bestTask); + + if(i == MAXPASS) + { + logAi->error("Goal %s exceeded maxpass. Terminating AI turn.", bestTask->toString()); + } } } diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index c3bd49b6d..f20ceda1f 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1359,7 +1359,7 @@ void VCAI::wander(HeroPtr h) TimeCheck tc("looking for wander destination"); - while(h->movementPointsRemaining()) + for(int k = 0; k < 10 && h->movementPointsRemaining(); k++) { validateVisitableObjs(); ah->updatePaths(getMyHeroes()); From 9bd27c50a0829f595b71b49a0d8515895ff5c673 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 6 Aug 2023 19:39:55 +0300 Subject: [PATCH 08/36] Allowed loading saves from inside mods --- client/Client.cpp | 2 +- client/lobby/SelectionTab.cpp | 2 +- server/CGameHandler.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/Client.cpp b/client/Client.cpp index 35e7c1649..eec33cb18 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -219,7 +219,7 @@ void CClient::loadGame(CGameState * initializedGameState) // try to deserialize client data including sleepingHeroes try { - boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::CLIENT_SAVEGAME)); + boost::filesystem::path clientSaveName = *CResourceHandler::get()->getResourceName(ResourceID(CSH->si->mapname, EResType::CLIENT_SAVEGAME)); if(clientSaveName.empty()) throw std::runtime_error("Cannot open client part of " + CSH->si->mapname); diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 84b430f32..09c4c6149 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -426,7 +426,7 @@ void SelectionTab::select(int position) if(inputName && inputName->isActive()) { - auto filename = *CResourceHandler::get("local")->getResourceName(ResourceID(curItems[py]->fileURI, EResType::SAVEGAME)); + auto filename = *CResourceHandler::get()->getResourceName(ResourceID(curItems[py]->fileURI, EResType::SAVEGAME)); inputName->setText(filename.stem().string()); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5b6385ac0..6308e1083 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2942,7 +2942,7 @@ bool CGameHandler::load(const std::string & filename) try { { - CLoadFile lf(*CResourceHandler::get("local")->getResourceName(ResourceID(stem.to_string(), EResType::SAVEGAME)), MINIMAL_SERIALIZATION_VERSION); + CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourceID(stem.to_string(), EResType::SAVEGAME)), MINIMAL_SERIALIZATION_VERSION); loadCommonState(lf); logGlobal->info("Loading server state"); lf >> *this; From c089e558939734fa7b3c098ee785cae1e31e9aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sun, 6 Aug 2023 21:14:16 +0200 Subject: [PATCH 09/36] Fix placing road inside underground rock --- lib/rmg/RmgArea.cpp | 6 ++++++ lib/rmg/RmgArea.h | 1 + lib/rmg/modificators/RoadPlacer.cpp | 10 ++++++++++ 3 files changed, 17 insertions(+) diff --git a/lib/rmg/RmgArea.cpp b/lib/rmg/RmgArea.cpp index 3131bf188..1b6b6196e 100644 --- a/lib/rmg/RmgArea.cpp +++ b/lib/rmg/RmgArea.cpp @@ -376,6 +376,12 @@ void Area::translate(const int3 & shift) //toAbsolute(dTiles, shift); } +void Area::erase_if(std::function predicate) +{ + invalidate(); + vstd::erase_if(dTiles, predicate); +} + Area operator- (const Area & l, const int3 & r) { Area result(l); diff --git a/lib/rmg/RmgArea.h b/lib/rmg/RmgArea.h index 1755f22d8..f07bcd5e0 100644 --- a/lib/rmg/RmgArea.h +++ b/lib/rmg/RmgArea.h @@ -64,6 +64,7 @@ namespace rmg void intersect(const Area & area); void subtract(const Area & area); void translate(const int3 & shift); + void erase_if(std::function predicate); friend Area operator+ (const Area & l, const int3 & r); //translation friend Area operator- (const Area & l, const int3 & r); //translation diff --git a/lib/rmg/modificators/RoadPlacer.cpp b/lib/rmg/modificators/RoadPlacer.cpp index 498c4fab0..c7e30e0a2 100644 --- a/lib/rmg/modificators/RoadPlacer.cpp +++ b/lib/rmg/modificators/RoadPlacer.cpp @@ -18,9 +18,12 @@ #include "../threadpool/MapProxy.h" #include "../../CModHandler.h" #include "../../mapping/CMapEditManager.h" +#include "../../TerrainHandler.h" VCMI_LIB_NAMESPACE_BEGIN +class TerrainType; + void RoadPlacer::process() { if(generator.getConfig().defaultRoadType.empty() && generator.getConfig().secondaryRoadType.empty()) @@ -114,6 +117,13 @@ void RoadPlacer::drawRoads(bool secondary) //Clean space under roads even if they won't be eventually generated Zone::Lock lock(zone.areaMutex); + //Do not draw roads on underground rock or water + roads.erase_if([this](const int3& pos) -> bool + { + const auto* terrain = map.getTile(pos).terType;; + return !terrain->isPassable() || !terrain->isLand(); + }); + zone.areaPossible().subtract(roads); zone.freePaths().unite(roads); } From f0ede46186514f67a0ab23c1fb652b09e50c95f2 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Aug 2023 19:12:04 +0300 Subject: [PATCH 10/36] Clear spell list, not hero army --- lib/mapping/MapFormatH3M.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 1df7ff008..e7ab538a9 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1736,7 +1736,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec { if(!object->spells.empty()) { - object->clear(); + object->spells.clear(); logGlobal->debug("Hero %s subID=%d has spells set twice (in map properties and on adventure map instance). Using the latter set...", object->getNameTextID(), object->subID); } From e57f8742cd888c80184283335751d1d855609a14 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Aug 2023 19:13:02 +0300 Subject: [PATCH 11/36] Rename ambiguos 'clear' to 'clearSlots' A lot of map objects inherit from CCreatureSet and as result - get clean() method that resets object army --- AI/Nullkiller/Analyzers/ArmyManager.cpp | 2 +- lib/CCreatureSet.cpp | 8 ++++---- lib/CCreatureSet.h | 6 +++--- lib/NetPacks.h | 2 +- lib/mapObjects/CBank.cpp | 2 +- mapeditor/inspector/rewardswidget.cpp | 2 +- server/HeroPoolProcessor.cpp | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/AI/Nullkiller/Analyzers/ArmyManager.cpp b/AI/Nullkiller/Analyzers/ArmyManager.cpp index 37dfb7a76..19f48ed65 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.cpp +++ b/AI/Nullkiller/Analyzers/ArmyManager.cpp @@ -170,7 +170,7 @@ std::vector ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, std::vector newArmy; uint64_t newValue = 0; - newArmyInstance.clear(); + newArmyInstance.clearSlots(); for(auto & slot : sortedSlots) { diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 084fb1e40..a11ede6ab 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -445,7 +445,7 @@ void CCreatureSet::setStackExp(const SlotID & slot, TExpType exp) stacks[slot]->experience = exp; } -void CCreatureSet::clear() +void CCreatureSet::clearSlots() { while(!stacks.empty()) { @@ -533,12 +533,12 @@ void CCreatureSet::changeStackCount(const SlotID & slot, TQuantity toAdd) CCreatureSet::~CCreatureSet() { - clear(); + clearSlots(); } void CCreatureSet::setToArmy(CSimpleArmy &src) { - clear(); + clearSlots(); while(src) { auto i = src.army.begin(); @@ -1050,7 +1050,7 @@ void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler) } } -void CSimpleArmy::clear() +void CSimpleArmy::clearSlots() { army.clear(); } diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 09794f3c8..4ef0e7ea7 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -181,7 +181,7 @@ using TCreatureQueue = std::priority_queue(config); - clear(); // remove all stacks, if any + clearSlots(); // remove all stacks, if any for(const auto & stack : config.guards) setCreature (SlotID(stacksCount()), stack.type->getId(), stack.count); diff --git a/mapeditor/inspector/rewardswidget.cpp b/mapeditor/inspector/rewardswidget.cpp index 64dcc9a78..ea2b94e9e 100644 --- a/mapeditor/inspector/rewardswidget.cpp +++ b/mapeditor/inspector/rewardswidget.cpp @@ -222,7 +222,7 @@ bool RewardsWidget::commitChanges() pandora->resources = ResourceSet(); pandora->artifacts.clear(); pandora->spells.clear(); - pandora->creatures.clear(); + pandora->creatures.clearSlots(); for(int row = 0; row < rewards; ++row) { diff --git a/server/HeroPoolProcessor.cpp b/server/HeroPoolProcessor.cpp index 2d50595b0..4c92e5963 100644 --- a/server/HeroPoolProcessor.cpp +++ b/server/HeroPoolProcessor.cpp @@ -112,7 +112,7 @@ void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroIns sah.slotID = selectSlotForRole(color, sah.roleID); sah.player = color; sah.hid = hero->subID; - sah.army.clear(); + sah.army.clearSlots(); sah.army.setCreature(SlotID(0), hero->type->initialArmy.at(0).creature, 1); gameHandler->sendAndApply(&sah); @@ -148,7 +148,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe else { sah.roleID = TavernSlotRole::SINGLE_UNIT; - sah.army.clear(); + sah.army.clearSlots(); sah.army.setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1); } } From 62a5eeebbcc42e56084b6c2f1ae447092eb285d3 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 7 Aug 2023 21:18:43 +0300 Subject: [PATCH 12/36] Fixed rendering priority of battlefield background obstacles --- client/battle/BattleObstacleController.cpp | 28 +++++++++++++++------- client/battle/BattleRenderer.h | 3 +-- client/battle/BattleSiegeController.cpp | 4 +--- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/client/battle/BattleObstacleController.cpp b/client/battle/BattleObstacleController.cpp index 09f46223a..d41b09a66 100644 --- a/client/battle/BattleObstacleController.cpp +++ b/client/battle/BattleObstacleController.cpp @@ -127,13 +127,26 @@ void BattleObstacleController::obstaclePlaced(const std::vectorcb->battleGetAllObstacles()) + for(auto & obstacle : owner.curInt->cb->battleGetAllObstacles()) { - if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) + if(obstacle->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) { - auto img = getObstacleImage(*oi); + auto img = getObstacleImage(*obstacle); if(img) - canvas.draw(img, Point(oi->getInfo().width, oi->getInfo().height)); + canvas.draw(img, Point(obstacle->getInfo().width, obstacle->getInfo().height)); + } + + if (obstacle->obstacleType == CObstacleInstance::USUAL) + { + if (obstacle->getInfo().isForegroundObstacle) + continue; + + auto img = getObstacleImage(*obstacle); + if(img) + { + Point p = getObstaclePosition(img, *obstacle); + canvas.draw(img, p); + } } } } @@ -148,11 +161,10 @@ void BattleObstacleController::collectRenderableObjects(BattleRenderer & rendere if (obstacle->obstacleType == CObstacleInstance::MOAT) continue; - bool isForeground = obstacle->obstacleType == CObstacleInstance::USUAL && obstacle->getInfo().isForegroundObstacle; + if (obstacle->obstacleType == CObstacleInstance::USUAL && !obstacle->getInfo().isForegroundObstacle) + continue; - auto layer = isForeground ? EBattleFieldLayer::OBSTACLES_FG : EBattleFieldLayer::OBSTACLES_BG; - - renderer.insert(layer, obstacle->pos, [this, obstacle]( BattleRenderer::RendererRef canvas ){ + renderer.insert(EBattleFieldLayer::OBSTACLES, obstacle->pos, [this, obstacle]( BattleRenderer::RendererRef canvas ){ auto img = getObstacleImage(*obstacle); if(img) { diff --git a/client/battle/BattleRenderer.h b/client/battle/BattleRenderer.h index fa5d4e15f..0dbab110b 100644 --- a/client/battle/BattleRenderer.h +++ b/client/battle/BattleRenderer.h @@ -16,12 +16,11 @@ class BattleInterface; enum class EBattleFieldLayer { // confirmed ordering requirements: - OBSTACLES_BG = 0, CORPSES = 0, WALLS = 1, HEROES = 2, STACKS = 2, // after corpses, obstacles, walls - OBSTACLES_FG = 3, // after stacks + OBSTACLES = 3, // after stacks STACK_AMOUNTS = 3, // after stacks, obstacles, corpses EFFECTS = 4, // after obstacles, battlements }; diff --git a/client/battle/BattleSiegeController.cpp b/client/battle/BattleSiegeController.cpp index 46852849c..63d5bd9e5 100644 --- a/client/battle/BattleSiegeController.cpp +++ b/client/battle/BattleSiegeController.cpp @@ -307,15 +307,13 @@ void BattleSiegeController::collectRenderableObjects(BattleRenderer & renderer) renderer.insert( EBattleFieldLayer::STACKS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){ owner.stacksController->showStack(canvas, getTurretStack(wallPiece)); }); - renderer.insert( EBattleFieldLayer::OBSTACLES_FG, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){ + renderer.insert( EBattleFieldLayer::OBSTACLES, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){ showWallPiece(canvas, wallPiece); }); } renderer.insert( EBattleFieldLayer::WALLS, getWallPiecePosition(wallPiece), [this, wallPiece](BattleRenderer::RendererRef canvas){ showWallPiece(canvas, wallPiece); }); - - } } From be6f6da3976bda8e215afeb5c2b9ecf01049b81f Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 8 Aug 2023 19:27:00 +0400 Subject: [PATCH 13/36] Fix for canRefuse flag in rewardable building --- lib/mapObjects/CGTownBuilding.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/mapObjects/CGTownBuilding.cpp b/lib/mapObjects/CGTownBuilding.cpp index 0cccdc62e..556bc91cb 100644 --- a/lib/mapObjects/CGTownBuilding.cpp +++ b/lib/mapObjects/CGTownBuilding.cpp @@ -356,6 +356,9 @@ void CTownRewardableBuilding::heroLevelUpDone(const CGHeroInstance *hero) const void CTownRewardableBuilding::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const { + if(answer == 0) + return; // player refused + if(visitors.find(hero->id) != visitors.end()) return; // query not for this building From e7394ad20cd2ce425aedf21186ec7cf726ff033c Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Tue, 8 Aug 2023 18:54:37 +0300 Subject: [PATCH 14/36] BattleAI: log time to make a decission --- AI/BattleAI/BattleAI.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index b3986de94..01947d4d5 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -254,6 +254,13 @@ void CBattleAI::yourTacticPhase(int distance) cb->battleMakeTacticAction(BattleAction::makeEndOFTacticPhase(cb->battleGetTacticsSide())); } +uint64_t timeElapsed(std::chrono::time_point start) +{ + auto end = std::chrono::high_resolution_clock::now(); + + return std::chrono::duration_cast(end - start).count(); +} + void CBattleAI::activeStack( const CStack * stack ) { LOG_TRACE_PARAMS(logAi, "stack: %s", stack->nodeName()); @@ -261,6 +268,8 @@ void CBattleAI::activeStack( const CStack * stack ) BattleAction result = BattleAction::makeDefend(stack); setCbc(cb); //TODO: make solid sure that AIs always use their callbacks (need to take care of event handlers too) + auto start = std::chrono::high_resolution_clock::now(); + try { if(stack->creatureId() == CreatureID::CATAPULT) @@ -276,6 +285,8 @@ void CBattleAI::activeStack( const CStack * stack ) attemptCastingSpell(); + logAi->trace("Spellcast attempt completed in %lld", timeElapsed(start)); + if(cb->battleIsFinished() || !stack->alive()) { //spellcast may finish battle or kill active stack @@ -312,6 +323,8 @@ void CBattleAI::activeStack( const CStack * stack ) movesSkippedByDefense = 0; } + logAi->trace("BattleAI decission made in %lld", timeElapsed(start)); + cb->battleMakeUnitAction(result); } From ba9998ac660ca01574139a8e074cbb864807163c Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Tue, 8 Aug 2023 18:38:41 +0300 Subject: [PATCH 15/36] BattleAI: fast targets optimization --- AI/BattleAI/BattleAI.cpp | 7 ++- AI/BattleAI/BattleExchangeVariant.cpp | 6 ++- AI/BattleAI/PotentialTargets.cpp | 11 ----- lib/spells/BattleSpellMechanics.cpp | 67 +++++++++++++++++++++++---- lib/spells/BattleSpellMechanics.h | 2 +- lib/spells/ISpellMechanics.cpp | 6 +-- lib/spells/ISpellMechanics.h | 4 +- test/mock/mock_spells_Mechanics.h | 2 +- 8 files changed, 74 insertions(+), 31 deletions(-) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 01947d4d5..0f7068402 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -507,7 +507,12 @@ void CBattleAI::attemptCastingSpell() { spells::BattleCast temp(cb.get(), hero, spells::Mode::HERO, spell); - for(auto & target : temp.findPotentialTargets()) + if(!spell->isDamage() && spell->getTargetType() == spells::AimType::LOCATION) + continue; + + const bool FAST = true; + + for(auto & target : temp.findPotentialTargets(FAST)) { PossibleSpellcast ps; ps.dest = target; diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index a1a4ddc24..d796134cd 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -600,6 +600,8 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb) if(unit->isTurret()) continue; + auto unitSpeed = unit->speed(turn); + if(turnBattle.battleCanShoot(unit)) { for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1) @@ -614,7 +616,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb) for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1) { - bool reachable = unitReachability.distances[hex] <= unit->speed(turn); + bool reachable = unitReachability.distances[hex] <= unitSpeed; if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK) { @@ -624,7 +626,7 @@ void BattleExchangeEvaluator::updateReachabilityMap(HypotheticBattle & hb) { for(BattleHex neighbor : hex.neighbouringTiles()) { - reachable = unitReachability.distances[neighbor] <= unit->speed(turn); + reachable = unitReachability.distances[neighbor] <= unitSpeed; if(reachable) break; } diff --git a/AI/BattleAI/PotentialTargets.cpp b/AI/BattleAI/PotentialTargets.cpp index d55e1dfa8..268350b5d 100644 --- a/AI/BattleAI/PotentialTargets.cpp +++ b/AI/BattleAI/PotentialTargets.cpp @@ -84,17 +84,6 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet { return lhs.damageDiff() > rhs.damageDiff(); }); - - if (!possibleAttacks.empty()) - { - auto & bestAp = possibleAttacks[0]; - - logGlobal->debug("Battle AI best: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld", - bestAp.attack.attacker->unitType()->getJsonKey(), - state.battleGetUnitByPos(bestAp.dest)->unitType()->getJsonKey(), - (int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(), - bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg); - } } int64_t PotentialTargets::bestActionValue() const diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index a09cf1965..82a16a276 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -590,7 +590,7 @@ std::vector BattleSpellMechanics::getTargetTypes() const return ret; } -std::vector BattleSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current) const +std::vector BattleSpellMechanics::getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const { //TODO: BattleSpellMechanics::getPossibleDestinations @@ -602,19 +602,66 @@ std::vector BattleSpellMechanics::getPossibleDestinations(size_t in switch(aimType) { case AimType::CREATURE: - case AimType::LOCATION: - for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) + { + auto stacks = battle()->battleGetAllStacks(); + + for(auto stack : stacks) { - BattleHex dest(i); - if(dest.isAvailable()) + Target tmp = current; + tmp.emplace_back(stack->getPosition()); + + detail::ProblemImpl ignored; + + if(canBeCastAt(tmp, ignored)) + ret.emplace_back(stack->getPosition()); + } + + break; + } + + case AimType::LOCATION: + if(fast) + { + auto stacks = battle()->battleGetAllStacks(); + std::set hexesToCheck; + + for(auto stack : stacks) { - Target tmp = current; - tmp.emplace_back(dest); + hexesToCheck.insert(stack->getPosition()); - detail::ProblemImpl ignored; + for(auto adjacent : stack->getPosition().neighbouringTiles()) + hexesToCheck.insert(adjacent); + } - if(canBeCastAt(tmp, ignored)) - ret.emplace_back(dest); + for(auto hex : hexesToCheck) + { + if(hex.isAvailable()) + { + Target tmp = current; + tmp.emplace_back(hex); + + detail::ProblemImpl ignored; + + if(canBeCastAt(tmp, ignored)) + ret.emplace_back(hex); + } + } + } + else + { + for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) + { + BattleHex dest(i); + if(dest.isAvailable()) + { + Target tmp = current; + tmp.emplace_back(dest); + + detail::ProblemImpl ignored; + + if(canBeCastAt(tmp, ignored)) + ret.emplace_back(dest); + } } } break; diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 7909da805..b89f1f444 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -50,7 +50,7 @@ public: /// Returns vector of all possible destinations for specified aim type /// index - ??? /// current - ??? - std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current) const override final; + std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const override final; /// Returns true if spell can be cast on unit bool isReceptive(const battle::Unit * target) const override; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 4f9d111be..f8709b882 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -326,7 +326,7 @@ bool BattleCast::castIfPossible(ServerCallback * server, Target target) return false; } -std::vector BattleCast::findPotentialTargets() const +std::vector BattleCast::findPotentialTargets(bool fast) const { //TODO: for more than 2 destinations per target much more efficient algorithm is required @@ -354,7 +354,7 @@ std::vector BattleCast::findPotentialTargets() const if(previous.empty()) { Target empty; - destinations = m->getPossibleDestinations(index, targetTypes.at(index), empty); + destinations = m->getPossibleDestinations(index, targetTypes.at(index), empty, fast); for(auto & destination : destinations) { @@ -367,7 +367,7 @@ std::vector BattleCast::findPotentialTargets() const { for(const Target & current : previous) { - destinations = m->getPossibleDestinations(index, targetTypes.at(index), current); + destinations = m->getPossibleDestinations(index, targetTypes.at(index), current, fast); for(auto & destination : destinations) { diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 2abce5b7b..e540bf20d 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -139,7 +139,7 @@ public: ///cast with silent check for permitted cast bool castIfPossible(ServerCallback * server, Target target); - std::vector findPotentialTargets() const; + std::vector findPotentialTargets(bool fast = false) const; private: ///spell school level @@ -199,7 +199,7 @@ public: virtual std::vector getTargetTypes() const = 0; - virtual std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current) const = 0; + virtual std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast = false) const = 0; virtual const Spell * getSpell() const = 0; diff --git a/test/mock/mock_spells_Mechanics.h b/test/mock/mock_spells_Mechanics.h index 05bc11118..e3efe9950 100644 --- a/test/mock/mock_spells_Mechanics.h +++ b/test/mock/mock_spells_Mechanics.h @@ -34,7 +34,7 @@ public: MOCK_CONST_METHOD1(isReceptive, bool(const battle::Unit * )); MOCK_CONST_METHOD0(getTargetTypes, std::vector()); - MOCK_CONST_METHOD3(getPossibleDestinations, std::vector(size_t, AimType, const Target &)); + MOCK_CONST_METHOD4(getPossibleDestinations, std::vector(size_t, AimType, const Target &, bool)); MOCK_CONST_METHOD0(getSpell, const Spell *()); From dc2135da7f2d803a3f7adbc0af823678fdafbf47 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 9 Aug 2023 00:46:55 +0300 Subject: [PATCH 16/36] Fix potential access to empty std function on hero vs hero combat --- client/battle/BattleInterfaceClasses.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index a6289d07f..7f2f0647d 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -636,7 +636,9 @@ void BattleResultWindow::show(Canvas & to) void BattleResultWindow::buttonPressed(int button) { - resultCallback(button); + if (resultCallback) + resultCallback(button); + CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon close(); From f483f7bb53eb9c69f4c57b1616e295f1506e1a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Thu, 10 Aug 2023 11:52:31 +0200 Subject: [PATCH 17/36] Fix crash at battle end --- lib/NetPacksLib.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index db8a2dd8d..9db793995 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -2265,6 +2265,9 @@ void BattleAttack::applyGs(CGameState * gs) void StartAction::applyGs(CGameState *gs) { + if (!gs->curB) + return; + CStack *st = gs->curB->getStack(ba.stackNumber); if(ba.actionType == EActionType::END_TACTIC_PHASE) From e9fb0c9b8c39d14d8afd08024bd9d28fa4021e2f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 10 Aug 2023 12:54:26 +0300 Subject: [PATCH 18/36] Make object fade-out / fade-in instant on instant movement speed --- client/mapView/MapViewController.cpp | 24 ++++++++++++++++++++---- client/mapView/MapViewController.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/client/mapView/MapViewController.cpp b/client/mapView/MapViewController.cpp index 7f29987f7..09f6589d6 100644 --- a/client/mapView/MapViewController.cpp +++ b/client/mapView/MapViewController.cpp @@ -247,6 +247,20 @@ void MapViewController::afterRender() } } +bool MapViewController::isEventInstant(const CGObjectInstance * obj) +{ + if (!isEventVisible(obj)) + return true; + + if(!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() <= 0) + return true; // instant movement speed + + if(LOCPLINT->makingTurn && settings["adventure"]["heroMoveTime"].Float() <= 0) + return true; // instant movement speed + + return false; +} + bool MapViewController::isEventVisible(const CGObjectInstance * obj) { if(adventureContext == nullptr) @@ -358,7 +372,8 @@ void MapViewController::onBeforeHeroEmbark(const CGHeroInstance * obj, const int { if(isEventVisible(obj, from, dest)) { - fadeOutObject(obj); + if (!isEventInstant(obj)) + fadeOutObject(obj); setViewCenter(obj->getSightCenter()); } else @@ -381,7 +396,8 @@ void MapViewController::onAfterHeroDisembark(const CGHeroInstance * obj, const i { if(isEventVisible(obj, from, dest)) { - fadeInObject(obj); + if (!isEventInstant(obj)) + fadeInObject(obj); setViewCenter(obj->getSightCenter()); } addObject(obj); @@ -391,7 +407,7 @@ void MapViewController::onObjectFadeIn(const CGObjectInstance * obj) { assert(!hasOngoingAnimations()); - if(isEventVisible(obj)) + if(isEventVisible(obj) && !isEventInstant(obj) ) fadeInObject(obj); addObject(obj); @@ -401,7 +417,7 @@ void MapViewController::onObjectFadeOut(const CGObjectInstance * obj) { assert(!hasOngoingAnimations()); - if(isEventVisible(obj)) + if(isEventVisible(obj) && !isEventInstant(obj) ) fadeOutObject(obj); else removeObject(obj); diff --git a/client/mapView/MapViewController.h b/client/mapView/MapViewController.h index 7b9941deb..bc67e90de 100644 --- a/client/mapView/MapViewController.h +++ b/client/mapView/MapViewController.h @@ -50,6 +50,7 @@ class MapViewController : public IMapObjectObserver std::shared_ptr puzzleMapContext; private: + bool isEventInstant(const CGObjectInstance * obj); bool isEventVisible(const CGObjectInstance * obj); bool isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest); From 84436f7f18a366d8c4d07131fbb5a909a09822ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Thu, 10 Aug 2023 18:29:49 +0200 Subject: [PATCH 19/36] Throw runtime error explicitely --- lib/NetPacksLib.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 9db793995..97187c890 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -38,6 +38,8 @@ VCMI_LIB_NAMESPACE_BEGIN +#define THROW_IF_NO_BATTLE if (!gs->curB) throw std::runtime_error("Trying to apply pack when no battle!"); + void CPack::visit(ICPackVisitor & visitor) { visitBasic(visitor); @@ -2127,16 +2129,19 @@ void BattleStart::applyGs(CGameState * gs) const void BattleNextRound::applyGs(CGameState * gs) const { + THROW_IF_NO_BATTLE gs->curB->nextRound(round); } void BattleSetActiveStack::applyGs(CGameState * gs) const { + THROW_IF_NO_BATTLE gs->curB->nextTurn(stack); } void BattleTriggerEffect::applyGs(CGameState * gs) const { + THROW_IF_NO_BATTLE CStack * st = gs->curB->getStack(stackID); assert(st); switch(static_cast(effect)) @@ -2232,6 +2237,7 @@ void BattleLogMessage::applyBattle(IBattleState * battleState) void BattleStackMoved::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2242,6 +2248,7 @@ void BattleStackMoved::applyBattle(IBattleState * battleState) void BattleStackAttacked::applyGs(CGameState * gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2252,6 +2259,7 @@ void BattleStackAttacked::applyBattle(IBattleState * battleState) void BattleAttack::applyGs(CGameState * gs) { + THROW_IF_NO_BATTLE CStack * attacker = gs->curB->getStack(stackAttacking); assert(attacker); @@ -2265,8 +2273,7 @@ void BattleAttack::applyGs(CGameState * gs) void StartAction::applyGs(CGameState *gs) { - if (!gs->curB) - return; + THROW_IF_NO_BATTLE CStack *st = gs->curB->getStack(ba.stackNumber); @@ -2316,7 +2323,7 @@ void StartAction::applyGs(CGameState *gs) void BattleSpellCast::applyGs(CGameState * gs) const { - assert(gs->curB); + THROW_IF_NO_BATTLE if(castByHero) { @@ -2329,6 +2336,7 @@ void BattleSpellCast::applyGs(CGameState * gs) const void SetStackEffect::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2347,6 +2355,7 @@ void SetStackEffect::applyBattle(IBattleState * battleState) void StacksInjured::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2358,6 +2367,7 @@ void StacksInjured::applyBattle(IBattleState * battleState) void BattleUnitsChanged::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2388,8 +2398,8 @@ void BattleUnitsChanged::applyBattle(IBattleState * battleState) void BattleObstaclesChanged::applyGs(CGameState * gs) { - if(gs->curB) - applyBattle(gs->curB); + THROW_IF_NO_BATTLE; + applyBattle(gs->curB); } void BattleObstaclesChanged::applyBattle(IBattleState * battleState) @@ -2420,8 +2430,8 @@ CatapultAttack::~CatapultAttack() = default; void CatapultAttack::applyGs(CGameState * gs) { - if(gs->curB) - applyBattle(gs->curB); + THROW_IF_NO_BATTLE + applyBattle(gs->curB); } void CatapultAttack::visitTyped(ICPackVisitor & visitor) @@ -2447,6 +2457,7 @@ void CatapultAttack::applyBattle(IBattleState * battleState) void BattleSetStackProperty::applyGs(CGameState * gs) const { + THROW_IF_NO_BATTLE CStack * stack = gs->curB->getStack(stackID); switch(which) { From 0252d0f98631d9d151dc7661de7a21a1c38f7347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Fri, 11 Aug 2023 07:45:24 +0200 Subject: [PATCH 20/36] Fix hota offset + 2 possible crashes --- lib/rmg/RmgObject.cpp | 10 ------ lib/rmg/modificators/ObjectManager.cpp | 49 +++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 0106bee4f..540463948 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -344,16 +344,6 @@ void Object::Instance::finalize(RmgMap & map) setTemplate(terrainType->getId()); } } - if (dObject.ID == Obj::MONSTER) - { - //Make up for extra offset in HotA creature templates - auto visitableOffset = dObject.getVisitableOffset(); - auto fixedPos = getPosition(true) + visitableOffset; - vstd::abetween(fixedPos.x, visitableOffset.x, map.width() - 1); - vstd::abetween(fixedPos.y, visitableOffset.y, map.height() - 1); - int3 parentPos = getPosition(true) - getPosition(false); - setPosition(fixedPos - parentPos); - } if (dObject.isVisitable() && !map.isOnMap(dObject.visitablePos())) throw rmgException(boost::to_string(boost::format("Visitable tile %s of object %d at %s is outside the map") % dObject.visitablePos().toString() % dObject.id % dObject.pos.toString())); diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 46f001c90..6644ee93f 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -129,6 +129,19 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object { float bestWeight = 0.f; int3 result(-1, -1, -1); + + //Blocked area might not cover object position if it has an offset from (0,0) + auto outsideTheMap = [this, &obj]() -> bool + { + for (const auto& oi : obj.instances()) + { + if (!map.isOnMap(oi->getPosition(true))) + { + return true; + } + } + return false; + }; if(optimizer & OptimizeType::DISTANCE) { @@ -149,6 +162,9 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea())) continue; + + if (outsideTheMap()) + continue; float weight = weightFunction(tile); if(weight > bestWeight) @@ -168,9 +184,12 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object if (obj.getVisibleTop().y < 0) continue; - + if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea())) continue; + + if (outsideTheMap()) + continue; float weight = weightFunction(tile); if(weight > bestWeight) @@ -416,7 +435,7 @@ bool ObjectManager::createRequiredObjects() //create object on specific positions //TODO: implement guards - for (const auto &objInfo : instantObjects) + for (const auto &objInfo : instantObjects) //Unused ATM { rmg::Object rmgObject(*objInfo.obj); rmgObject.setPosition(objInfo.pos); @@ -435,6 +454,21 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD { object.finalize(map); + if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER) + { + //Fix for HoTA offset - lonely guards + object.getPosition(); + auto monster = object.instances().front(); + auto visitableOffset = monster->object().getVisitableOffset(); + auto fixedPos = monster->getPosition(true) + visitableOffset; + + //Do not place guard outside the map + vstd::abetween(fixedPos.x, visitableOffset.x, map.width() - 1); + vstd::abetween(fixedPos.y, visitableOffset.y, map.height() - 1); + int3 parentOffset = monster->getPosition(true) - monster->getPosition(false); + monster->setPosition(fixedPos - parentOffset); + } + Zone::Lock lock(zone.areaMutex); zone.areaPossible().subtract(object.getArea()); bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition()); @@ -443,8 +477,8 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD zone.freePaths().add(object.getVisitablePosition()); zone.areaUsed().unite(object.getArea()); zone.areaUsed().erase(object.getVisitablePosition()); - - if(guarded) + + if(guarded) //We assume the monster won't be guarded { auto guardedArea = object.instances().back()->getAccessibleArea(); guardedArea.add(object.instances().back()->getVisitablePosition()); @@ -501,6 +535,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD { case Obj::RANDOM_TREASURE_ART: case Obj::RANDOM_MINOR_ART: //In OH3 quest artifacts have higher value than normal arts + case Obj::RANDOM_RESOURCE: { if (auto * qap = zone.getModificator()) { @@ -629,8 +664,12 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard }); auto & instance = object.addInstance(*guard); - instance.setPosition(guardPos - object.getPosition()); instance.setAnyTemplate(); //terrain is irrelevant for monsters, but monsters need some template now + + //Fix HoTA monsters with offset template + auto visitableOffset = instance.object().getVisitableOffset(); + auto fixedPos = guardPos - object.getPosition() + visitableOffset; + instance.setPosition(fixedPos); return true; } From 213eda5e1ee84e0eb7345650e5343f274cce3ca5 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Fri, 11 Aug 2023 14:19:06 +0300 Subject: [PATCH 21/36] altar sacrifice all fix --- client/windows/CTradeWindow.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 6787308fd..7787ec4de 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -1262,11 +1262,14 @@ void CAltarWindow::SacrificeAll() } else { - for(const auto & aw : arts->visibleArtSet.artifactsWorn) + std::vector> artsForMove; + for(const auto& slotInfo : arts->visibleArtSet.artifactsWorn) { - if(!aw.second.locked) - moveArtToAltar(nullptr, aw.second.artifact); + if(!slotInfo.second.locked && slotInfo.second.artifact->artType->isTradable()) + artsForMove.push_back(slotInfo.second.artifact); } + for(auto artInst : artsForMove) + moveArtToAltar(nullptr, artInst); arts->updateWornSlots(); SacrificeBackpack(); } From 1b531c67f5b4d4ffd11b5f0d92b4b90fff5dc088 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 11 Aug 2023 15:06:02 +0300 Subject: [PATCH 22/36] Added option to configure reserved screen area on mobile platforms --- launcher/settingsView/csettingsview_moc.cpp | 16 +- launcher/settingsView/csettingsview_moc.h | 2 + launcher/settingsView/csettingsview_moc.ui | 911 ++++++++++---------- 3 files changed, 482 insertions(+), 447 deletions(-) diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index c4bbb9eec..9f00a369f 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/launcher/settingsView/csettingsview_moc.cpp @@ -72,6 +72,8 @@ void CSettingsView::loadSettings() ui->comboBoxFullScreen->hide(); ui->labelFullScreen->hide(); #else + ui->labelReservedArea->hide(); + ui->spinBoxReservedArea->hide(); if (settings["video"]["realFullscreen"].Bool()) ui->comboBoxFullScreen->setCurrentIndex(2); else @@ -81,6 +83,7 @@ void CSettingsView::loadSettings() ui->spinBoxInterfaceScaling->setValue(settings["video"]["resolution"]["scaling"].Float()); ui->spinBoxFramerateLimit->setValue(settings["video"]["targetfps"].Float()); + ui->spinBoxReservedArea->setValue(std::round(settings["video"]["reservedWidth"].Float() * 100)); ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String())); ui->comboBoxNeutralAI->setCurrentText(QString::fromStdString(settings["server"]["neutralAI"].String())); @@ -468,14 +471,12 @@ void CSettingsView::on_lineEditRepositoryExtra_textEdited(const QString &arg1) node->String() = arg1.toStdString(); } - void CSettingsView::on_spinBoxInterfaceScaling_valueChanged(int arg1) { Settings node = settings.write["video"]["resolution"]["scaling"]; node->Float() = arg1; } - void CSettingsView::on_refreshRepositoriesButton_clicked() { auto * mainWindow = dynamic_cast(qApp->activeWindow()); @@ -487,7 +488,6 @@ void CSettingsView::on_refreshRepositoriesButton_clicked() mainWindow->getModView()->loadRepositories(); } - void CSettingsView::on_spinBoxFramerateLimit_valueChanged(int arg1) { Settings node = settings.write["video"]["targetfps"]; @@ -500,14 +500,12 @@ void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &a node->String() = arg1.toUtf8().data(); } - void CSettingsView::on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1) { Settings node = settings.write["server"]["alliedAI"]; node->String() = arg1.toUtf8().data(); } - void CSettingsView::on_checkBoxAutoSavePrefix_stateChanged(int arg1) { Settings node = settings.write["general"]["useSavePrefix"]; @@ -515,17 +513,21 @@ void CSettingsView::on_checkBoxAutoSavePrefix_stateChanged(int arg1) ui->lineEditAutoSavePrefix->setEnabled(arg1); } - void CSettingsView::on_spinBoxAutoSaveLimit_valueChanged(int arg1) { Settings node = settings.write["general"]["autosaveCountLimit"]; node->Float() = arg1; } - void CSettingsView::on_lineEditAutoSavePrefix_textEdited(const QString &arg1) { Settings node = settings.write["general"]["savePrefix"]; node->String() = arg1.toStdString(); } +void CSettingsView::on_spinBoxReservedArea_valueChanged(int arg1) +{ + Settings node = settings.write["video"]["reservedWidth"]; + node->Float() = float(arg1) / 100; // percentage -> ratio +} + diff --git a/launcher/settingsView/csettingsview_moc.h b/launcher/settingsView/csettingsview_moc.h index 6e27d9896..5cc14c505 100644 --- a/launcher/settingsView/csettingsview_moc.h +++ b/launcher/settingsView/csettingsview_moc.h @@ -72,6 +72,8 @@ private slots: void on_lineEditAutoSavePrefix_textEdited(const QString &arg1); + void on_spinBoxReservedArea_valueChanged(int arg1); + private: Ui::CSettingsView * ui; diff --git a/launcher/settingsView/csettingsview_moc.ui b/launcher/settingsView/csettingsview_moc.ui index 5e3f43e84..5924e4f70 100644 --- a/launcher/settingsView/csettingsview_moc.ui +++ b/launcher/settingsView/csettingsview_moc.ui @@ -42,6 +42,7 @@ + 75 true @@ -106,35 +107,136 @@ 0 - 0 + -197 610 - 790 + 768 - - + + + + + + + + + + + false + + + BattleAI + + + + BattleAI + + + + + StupidAI + + + + + + + + Display index + + + + + + + VCAI + + + + VCAI + + + + + Nullkiller + + + + + + + + Autosave limit (0 = off) + + + + + + + Fullscreen + + + + + + + Network port + + + + + + 75 true - Artificial Intelligence + General - - + + - + Autosave - + + + + + + + Hardware + + + + + Software + + + + + + + + Show intro + + + + + + + + + true - + true @@ -147,13 +249,358 @@ - - + + + + 20 + + + 1000 + + + 10 + + + + + + + + + + Framerate Limit + + + + + + + + + + Adventure Map Enemies + + + + + + + Default repository + + + + + + + + + + 1 + + + + Off + + + + + On + + + + + + + + + 75 + true + + + + Video + + + + + + + Autosave prefix + + + + + + + Resolution + + + + + + + + + + + + + + + + + true + + + + + + + Cursor + + + + + + + 1 + + + + Off + + + + + On + + + + + + + + Refresh now + + + + + - - true + + + + + + Heroes III Translation + + + + + + + 1 + + + + Off + + + + + On + + + + + + + + Enemy AI in battles + + + + + + + + 75 + true + + + + Mod Repositories + + + + + + + Additional repository + + + + + + + false + + + BattleAI + + + + BattleAI + + + + + StupidAI + + + + + + + + 50 + + + 400 + + + 10 + + + + + + + Heroes III Data Language + + + + + + + + + + Neutral AI in battles + + + + + + + Interface Scaling + + + + + + + 1024 + + + 65535 + + + 3030 + + + + + + + + + + + + + + Friendly AI in battles + + + + + + + VCMI Language + + + + + + + + + + Check on startup + + + + + + + BattleAI + + + + BattleAI + + + + + StupidAI + + + + + + + + Adventure Map Allies + + + + + + + + 75 + true + + + + Artificial Intelligence + + + + + + + VCAI + + + + VCAI + + + + + Nullkiller + + + + + + + + empty = map name prefix @@ -188,445 +635,29 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - - + + - Resolution - - - - - - - Adventure Map Allies - - - - - - - false - - - BattleAI - - - - BattleAI - - - - - StupidAI - - - - - - - - - true - - - - Mod Repositories - - - - - - - 1 - - - - Off - - - - - On - - - - - - - - - - - - - - - Default repository - - - - - - - Additional repository - - - - - - - Neutral AI in battles - - - - - - - + Reserved screen area - + + + % + - 50 + 0 - 400 + 25 - 10 - - - - - - - Interface Scaling - - - - - - - Heroes III Data Language - - - - - - - Autosave limit (0 = off) - - - - - - - - - - Friendly AI in battles - - - - - - - false - - - BattleAI - - - - BattleAI - - - - - StupidAI - - - - - - - - - - - VCAI - - - - VCAI - - - - - Nullkiller - - - - - - - - Display index - - - - - - - Adventure Map Enemies - - - - - - - 20 - - - 1000 - - - 10 - - - - - - - Show intro - - - - - - 1 - - - Off - - - - - On - - - - - - - - - - - - Hardware - - - - - Software - - - - - - - - Fullscreen - - - - - - - Cursor - - - - - - - - - - - - - - Enemy AI in battles - - - - - - - Check on startup - - - - - - - - true - - - - General - - - - - - - BattleAI - - - - BattleAI - - - - - StupidAI - - - - - - - - Refresh now - - - - - - - 1024 - - - 65535 - - 3030 - - - - - - - Network port - - - - - - - Heroes III Translation - - - - - - - VCMI Language - - - - - - - Autosave - - - - - - - - true - - - - Video - - - - - - - VCAI - - - - VCAI - - - - - Nullkiller - - - - - - - - - - - Framerate Limit - - - - - - - 1 - - - - Off - - - - - On - - - - - - - - Autosave prefix - - - - - - - - - - - - - - - - - empty = map name prefix + 0 From c0e51fc13e270e21bdcf2ddbbe5f1bd530a7fb5f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 11 Aug 2023 15:08:22 +0300 Subject: [PATCH 23/36] Update translations --- launcher/settingsView/csettingsview_moc.ui | 2 +- launcher/translation/chinese.ts | 103 +++++++++++---------- launcher/translation/english.ts | 103 +++++++++++---------- launcher/translation/french.ts | 103 +++++++++++---------- launcher/translation/german.ts | 103 +++++++++++---------- launcher/translation/polish.ts | 103 +++++++++++---------- launcher/translation/russian.ts | 103 +++++++++++---------- launcher/translation/spanish.ts | 103 +++++++++++---------- launcher/translation/ukrainian.ts | 103 +++++++++++---------- 9 files changed, 433 insertions(+), 393 deletions(-) diff --git a/launcher/settingsView/csettingsview_moc.ui b/launcher/settingsView/csettingsview_moc.ui index 5924e4f70..186689a6c 100644 --- a/launcher/settingsView/csettingsview_moc.ui +++ b/launcher/settingsView/csettingsview_moc.ui @@ -645,7 +645,7 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - % + % 0 diff --git a/launcher/translation/chinese.ts b/launcher/translation/chinese.ts index 8ac241e96..6bdd419ce 100644 --- a/launcher/translation/chinese.ts +++ b/launcher/translation/chinese.ts @@ -407,123 +407,123 @@ CSettingsView - - - + + + Off 关闭 - - + + Artificial Intelligence 人工智能 - - + + Mod Repositories 模组仓库 - + Interface Scaling - + Neutral AI in battles - + Enemy AI in battles - + Additional repository - + Adventure Map Allies - + Adventure Map Enemies - + Windowed - + Borderless fullscreen - + Exclusive fullscreen - + Autosave limit (0 = off) - + Friendly AI in battles - + Framerate Limit - + Autosave prefix - + empty = map name prefix - + Refresh now - + Default repository - - - + + + On 开启 - + Cursor 鼠标指针 - + Heroes III Data Language 英雄无敌3数据语言 - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -534,95 +534,100 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - + + Reserved screen area + + + + Hardware 硬件 - + Software 软件 - + Heroes III Translation 发布版本里找不到这个项,不太清楚意义 英雄无敌3翻译 - + Check on startup 启动时检查更新 - + Fullscreen 全屏 - - + + General 通用设置 - + VCMI Language VCMI语言 - + Resolution 分辨率 - + Autosave 自动存档 - + Display index 显示器序号 - + Network port 网络端口 - - + + Video 视频设置 - + Show intro 显示开场动画 - + Active 激活 - + Disabled 禁用 - + Enable 启用 - + Not Installed 未安装 - + Install 安装 diff --git a/launcher/translation/english.ts b/launcher/translation/english.ts index 9270e252a..4d6ad1deb 100644 --- a/launcher/translation/english.ts +++ b/launcher/translation/english.ts @@ -406,123 +406,123 @@ CSettingsView - - - + + + Off - - + + Artificial Intelligence - - + + Mod Repositories - + Interface Scaling - + Neutral AI in battles - + Enemy AI in battles - + Additional repository - + Adventure Map Allies - + Adventure Map Enemies - + Windowed - + Borderless fullscreen - + Exclusive fullscreen - + Autosave limit (0 = off) - + Friendly AI in battles - + Framerate Limit - + Autosave prefix - + empty = map name prefix - + Refresh now - + Default repository - - - + + + On - + Cursor - + Heroes III Data Language - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -533,94 +533,99 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - + + Reserved screen area + + + + Hardware - + Software - + Heroes III Translation - + Check on startup - + Fullscreen - - + + General - + VCMI Language - + Resolution - + Autosave - + Display index - + Network port - - + + Video - + Show intro - + Active - + Disabled - + Enable - + Not Installed - + Install diff --git a/launcher/translation/french.ts b/launcher/translation/french.ts index 110fbcbbd..e59b15e82 100644 --- a/launcher/translation/french.ts +++ b/launcher/translation/french.ts @@ -411,43 +411,43 @@ CSettingsView - - - + + + Off Désactivé - - + + Artificial Intelligence Intelligence Artificielle - - + + Mod Repositories Dépôts de Mod - - - + + + On Activé - + Enemy AI in battles IA ennemie dans les batailles - + Default repository Dépôt par défaut - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -464,174 +464,179 @@ Mode fenêtré sans bord - le jeu s"exécutera dans une fenêtre qui couvre Mode exclusif plein écran - le jeu couvrira l"intégralité de votre écran et utilisera la résolution sélectionnée. - + Windowed Fenêtré - + Borderless fullscreen Fenêtré sans bord - + Exclusive fullscreen Plein écran exclusif - + + Reserved screen area + + + + Neutral AI in battles IA neutre dans les batailles - + Autosave limit (0 = off) - + Adventure Map Enemies Ennemis de la carte d"aventure - + Autosave prefix - + empty = map name prefix - + Interface Scaling Mise à l"échelle de l"interface - + Cursor Curseur - + Heroes III Data Language Langue des Données de Heroes III - + Framerate Limit Limite de fréquence d"images - + Hardware Matériel - + Software Logiciel - + Heroes III Translation Traduction de Heroes III - + Adventure Map Allies Alliés de la carte d"aventure - + Additional repository Dépôt supplémentaire - + Check on startup Vérifier au démarrage - + Refresh now Actualiser maintenant - + Friendly AI in battles IA amicale dans les batailles - + Fullscreen Plein écran - - + + General Général - + VCMI Language Langue de VCMI - + Resolution Résolution - + Autosave Sauvegarde automatique - + Display index Index d'affichage - + Network port Port de réseau - - + + Video Vidéo - + Show intro Montrer l'intro - + Active Actif - + Disabled Désactivé - + Enable Activé - + Not Installed Pas Installé - + Install Installer diff --git a/launcher/translation/german.ts b/launcher/translation/german.ts index 2840fb21f..fd803264f 100644 --- a/launcher/translation/german.ts +++ b/launcher/translation/german.ts @@ -406,123 +406,123 @@ CSettingsView - - - + + + Off Aus - - + + Artificial Intelligence Künstliche Intelligenz - - + + Mod Repositories Mod-Repositorien - + Interface Scaling Skalierung der Benutzeroberfläche - + Neutral AI in battles Neutrale KI in Kämpfen - + Enemy AI in battles Gegnerische KI in Kämpfen - + Additional repository Zusätzliches Repository - + Adventure Map Allies Abenteuerkarte Verbündete - + Adventure Map Enemies Abenteuerkarte Feinde - + Windowed Fenstermodus - + Borderless fullscreen Randloser Vollbildmodus - + Exclusive fullscreen Exklusiver Vollbildmodus - + Autosave limit (0 = off) Limit für Autospeicherung (0 = aus) - + Friendly AI in battles Freundliche KI in Kämpfen - + Framerate Limit Limit der Bildrate - + Autosave prefix Präfix für Autospeicherung - + empty = map name prefix leer = Kartenname als Präfix - + Refresh now Jetzt aktualisieren - + Default repository Standard Repository - - - + + + On An - + Cursor Zeiger - + Heroes III Data Language Sprache der Heroes III Daten - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -539,94 +539,99 @@ Randloser Fenstermodus - das Spiel läuft in einem Fenster, das den gesamten Bil Exklusiver Vollbildmodus - das Spiel bedeckt den gesamten Bildschirm und verwendet die gewählte Auflösung. - + + Reserved screen area + + + + Hardware Hardware - + Software Software - + Heroes III Translation Heroes III Übersetzung - + Check on startup Beim Start prüfen - + Fullscreen Vollbild - - + + General Allgemein - + VCMI Language VCMI-Sprache - + Resolution Auflösung - + Autosave Autospeichern - + Display index Anzeige-Index - + Network port Netzwerk-Port - - + + Video Video - + Show intro Intro anzeigen - + Active Aktiv - + Disabled Deaktiviert - + Enable Aktivieren - + Not Installed Nicht installiert - + Install Installieren diff --git a/launcher/translation/polish.ts b/launcher/translation/polish.ts index d72e7c62e..75ea0498f 100644 --- a/launcher/translation/polish.ts +++ b/launcher/translation/polish.ts @@ -406,123 +406,123 @@ CSettingsView - - - + + + Off Wyłączony - - + + Artificial Intelligence Sztuczna Inteligencja - - + + Mod Repositories Repozytoria modów - + Interface Scaling Skala interfejsu - + Neutral AI in battles AI bitewne jednostek neutralnych - + Enemy AI in battles AI bitewne wrogów - + Additional repository Dodatkowe repozytorium - + Adventure Map Allies AI sojuszników mapy przygody - + Adventure Map Enemies AI wrogów mapy przygody - + Windowed Okno - + Borderless fullscreen Pełny ekran (tryb okna) - + Exclusive fullscreen Pełny ekran klasyczny - + Autosave limit (0 = off) Limit autozapisów (0 = brak) - + Friendly AI in battles AI bitewne sojuszników - + Framerate Limit Limit FPS - + Autosave prefix Przedrostek autozapisu - + empty = map name prefix puste = przedrostek z nazwy mapy - + Refresh now Odśwież - + Default repository Domyślne repozytorium - - - + + + On Włączony - + Cursor Kursor - + Heroes III Data Language Język plików Heroes III - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -539,94 +539,99 @@ Pełny ekran w trybie okna - gra uruchomi się w oknie przysłaniającym cały e Pełny ekran klasyczny - gra przysłoni cały ekran uruchamiając się w wybranej przez ciebie rozdzielczości ekranu. - + + Reserved screen area + + + + Hardware Sprzętowy - + Software Programowy - + Heroes III Translation Tłumaczenie Heroes III - + Check on startup Sprawdzaj przy uruchomieniu - + Fullscreen Pełny ekran - - + + General Ogólne - + VCMI Language Język VCMI - + Resolution Rozdzielczość - + Autosave Autozapis - + Display index Numer wyświetlacza - + Network port Port sieciowy - - + + Video Obraz - + Show intro Pokaż intro - + Active Aktywny - + Disabled Wyłączone - + Enable Włącz - + Not Installed Nie zainstalowano - + Install Zainstaluj diff --git a/launcher/translation/russian.ts b/launcher/translation/russian.ts index 7e0953a29..a5ef0dd5c 100644 --- a/launcher/translation/russian.ts +++ b/launcher/translation/russian.ts @@ -406,144 +406,149 @@ CSettingsView - + Interface Scaling - - - + + + Off Отключено - - - + + + On Включено - + Neutral AI in battles - + Enemy AI in battles - + Additional repository - + Check on startup Проверять при запуске - + Fullscreen Полноэкранный режим - - + + General Общее - + VCMI Language Язык VCMI - + Cursor Курсор - - + + Artificial Intelligence Искусственный интеллект - - + + Mod Repositories Репозитории модов - + Adventure Map Allies - + Refresh now - + Adventure Map Enemies - + Windowed - + Borderless fullscreen - + Exclusive fullscreen - + + Reserved screen area + + + + Autosave limit (0 = off) - + Friendly AI in battles - + Framerate Limit - + Autosave prefix - + empty = map name prefix - + Default repository - + Heroes III Data Language Язык данных Героев III - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -554,73 +559,73 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - + Hardware Аппаратный - + Software Программный - + Heroes III Translation Перевод Героев III - + Resolution Разрешение экрана - + Autosave Автосохранение - + Display index Дисплей - + Network port Сетевой порт - - + + Video Графика - + Show intro Вступление - + Active Активен - + Disabled Отключен - + Enable Включить - + Not Installed Не установлен - + Install Установить diff --git a/launcher/translation/spanish.ts b/launcher/translation/spanish.ts index 6401189bc..04499cedf 100644 --- a/launcher/translation/spanish.ts +++ b/launcher/translation/spanish.ts @@ -406,165 +406,170 @@ CSettingsView - - - + + + Off Desactivado - - + + Artificial Intelligence Inteligencia Artificial - - + + Mod Repositories Repositorios de Mods - + Interface Scaling - + Neutral AI in battles - + Enemy AI in battles - + Additional repository - + Adventure Map Allies - + Adventure Map Enemies - + Windowed - + Borderless fullscreen - + Exclusive fullscreen - + Autosave limit (0 = off) - + Friendly AI in battles - + Framerate Limit - + Autosave prefix - + empty = map name prefix - + Refresh now - + Default repository - - - + + + On Encendido - + Cursor Cursor - + Heroes III Translation Traducción de Heroes III - + + Reserved screen area + + + + Fullscreen Pantalla completa - - + + General General - + VCMI Language Idioma de VCMI - + Resolution Resolución - + Autosave Autoguardado - + Display index Mostrar índice - + Network port Puerto de red - - + + Video Vídeo - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -575,52 +580,52 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use - + Hardware Hardware - + Software Software - + Show intro Mostrar introducción - + Check on startup Comprovar al inicio - + Heroes III Data Language Idioma de los datos de Heroes III. - + Active Activado - + Disabled Desactivado - + Enable Activar - + Not Installed No Instalado - + Install Instalar diff --git a/launcher/translation/ukrainian.ts b/launcher/translation/ukrainian.ts index b4362d867..d3015e426 100644 --- a/launcher/translation/ukrainian.ts +++ b/launcher/translation/ukrainian.ts @@ -406,123 +406,123 @@ CSettingsView - - - + + + Off Вимкнено - - + + Artificial Intelligence Штучний інтелект - - + + Mod Repositories Репозиторії модифікацій - + Interface Scaling Масштабування інтерфейсу - + Neutral AI in battles Нейтральний ШІ в боях - + Enemy AI in battles Ворожий ШІ в боях - + Additional repository Додатковий репозиторій - + Adventure Map Allies Союзники на мапі пригод - + Adventure Map Enemies Вороги на мапі пригод - + Windowed У вікні - + Borderless fullscreen Повноекранне вікно - + Exclusive fullscreen Повноекранний (ексклюзивно) - + Autosave limit (0 = off) Кількість автозбережень - + Friendly AI in battles Дружній ШІ в боях - + Framerate Limit Обмеження частоти кадрів - + Autosave prefix Префікс назв автозбережень - + empty = map name prefix (використовувати назву карти) - + Refresh now Оновити зараз - + Default repository Стандартний репозиторій - - - + + + On Увімкнено - + Cursor Курсор - + Heroes III Data Language Мова Heroes III - + Select display mode for game Windowed - game will run inside a window that covers part of your screen @@ -539,94 +539,99 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use Повноекранний ексклюзивний режим - гра займатиме весь екран і використовуватиме вибрану роздільну здатність. - + + Reserved screen area + Зарезервована зона екрану + + + Hardware Апаратний - + Software Програмний - + Heroes III Translation Переклад Heroes III - + Check on startup Перевіряти на старті - + Fullscreen Повноекранний режим - - + + General Загальні налаштування - + VCMI Language Мова VCMI - + Resolution Роздільна здатність - + Autosave Автозбереження - + Display index Дісплей - + Network port Мережевий порт - - + + Video Графіка - + Show intro Вступні відео - + Active Активні - + Disabled Деактивований - + Enable Активувати - + Not Installed Не встановлено - + Install Встановити From 474045194aeb009dba710964bb9e10296db1a521 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 11 Aug 2023 15:59:35 +0300 Subject: [PATCH 24/36] Do not ignore translations for maps & campaigns --- lib/CGeneralTextHandler.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index eb2cd8900..5739db44f 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -313,19 +313,7 @@ void CGeneralTextHandler::registerStringOverride(const std::string & modContext, assert(!modContext.empty()); assert(!language.empty()); - std::string baseModLanguage = getModLanguage(modContext); - - if (baseModLanguage != language) - { - // this is translation - only add text to existing strings, do not register new ones - if (stringsLocalizations.count(UID.get()) == 0) - { - logMod->warn("Unknown string '%s' in mod '%s' for language '%s'. Ignoring", UID.get(), modContext, language); - return; - } - } - - // NOTE: implicitly creates entry, intended - strings added by vcmi (and potential UI mods) are not registered anywhere at the moment + // NOTE: implicitly creates entry, intended - strings added by maps, campaigns, vcmi and potentially - UI mods are not registered anywhere at the moment auto & entry = stringsLocalizations[UID.get()]; entry.overrideLanguage = language; @@ -349,6 +337,9 @@ bool CGeneralTextHandler::validateTranslation(const std::string & language, cons if (string.second.baseLanguage == language && !string.second.baseValue.empty()) continue; // Base string already uses our language + if (string.second.baseLanguage.empty()) + continue; // String added in localization, not present in base language (e.g. maps/campaigns) + if (config.Struct().count(string.first) > 0) continue; From 775e5948ec4cb2dfb945814319d7004f1354384b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 11 Aug 2023 16:53:02 +0300 Subject: [PATCH 25/36] Preserve chosen campaign bonus on scenario restart request --- server/CVCMIServer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 8a9ad301e..234ee09ab 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -277,6 +277,12 @@ void CVCMIServer::prepareToRestart() * si = * gh->gs->initialOpts; si->seedToBeUsed = si->seedPostInit = 0; state = EServerState::LOBBY; + if (si->campState) + { + assert(si->campState->currentScenario().has_value()); + campaignMap = si->campState->currentScenario().value_or(CampaignScenarioID(0)); + campaignBonus = si->campState->getBonusID(campaignMap).value_or(-1); + } // FIXME: dirry hack to make sure old CGameHandler::run is finished boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); } From 8f450cf253dea640f3320a972c6ba80e1a8bd4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Fri, 11 Aug 2023 18:43:22 +0200 Subject: [PATCH 26/36] Fix for monsters spawning at left side of the map --- lib/rmg/modificators/ObjectManager.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 6644ee93f..d1afd557e 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -452,13 +452,19 @@ bool ObjectManager::createRequiredObjects() void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/) { - object.finalize(map); + //object.finalize(map); if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER) { //Fix for HoTA offset - lonely guards - object.getPosition(); + auto monster = object.instances().front(); + if (!monster->object().appearance) + { + //Needed to determine visitable offset + monster->setAnyTemplate(); + } + object.getPosition(); auto visitableOffset = monster->object().getVisitableOffset(); auto fixedPos = monster->getPosition(true) + visitableOffset; @@ -468,6 +474,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD int3 parentOffset = monster->getPosition(true) - monster->getPosition(false); monster->setPosition(fixedPos - parentOffset); } + object.finalize(map); Zone::Lock lock(zone.areaMutex); zone.areaPossible().subtract(object.getArea()); From 4741bd546fd3c10255b4737a5729d1f2c874bad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Fri, 11 Aug 2023 20:13:25 +0200 Subject: [PATCH 27/36] Fix regression --- lib/rmg/modificators/QuestArtifactPlacer.cpp | 10 ++++++---- lib/rmg/threadpool/MapProxy.cpp | 6 ++++++ lib/rmg/threadpool/MapProxy.h | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/rmg/modificators/QuestArtifactPlacer.cpp b/lib/rmg/modificators/QuestArtifactPlacer.cpp index 7640474f3..fd9fe44d8 100644 --- a/lib/rmg/modificators/QuestArtifactPlacer.cpp +++ b/lib/rmg/modificators/QuestArtifactPlacer.cpp @@ -89,14 +89,15 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand) artifactToReplace->getObjectName(), artifactToReplace->getPosition().toString(), VLC->artifacts()->getById(artifactToPlace)->getNameTranslated()); - artifactToReplace->ID = Obj::ARTIFACT; - artifactToReplace->subID = artifactToPlace; //Update appearance. Terrain is irrelevant. auto handler = VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, artifactToPlace); + auto newObj = handler->create(); auto templates = handler->getTemplates(); - artifactToReplace->appearance = templates.front(); - //FIXME: Instance name is still "randomArtifact" + //artifactToReplace->appearance = templates.front(); + newObj->appearance = templates.front(); + newObj->pos = artifactToReplace->pos; + mapProxy->insertObject(newObj); for (auto z : map.getZones()) { @@ -107,6 +108,7 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand) localQap->dropReplacedArtifact(artifactToReplace); } } + mapProxy->removeObject(artifactToReplace); break; } } diff --git a/lib/rmg/threadpool/MapProxy.cpp b/lib/rmg/threadpool/MapProxy.cpp index 7ae73fe9b..d1bf2f8ee 100644 --- a/lib/rmg/threadpool/MapProxy.cpp +++ b/lib/rmg/threadpool/MapProxy.cpp @@ -30,6 +30,12 @@ void MapProxy::insertObjects(std::set& objects) map.getEditManager()->insertObjects(objects); } +void MapProxy::removeObject(CGObjectInstance * obj) +{ + Lock lock(mx); + map.getEditManager()->removeObject(obj); +} + void MapProxy::drawTerrain(CRandomGenerator & generator, std::vector & tiles, TerrainId terrain) { Lock lock(mx); diff --git a/lib/rmg/threadpool/MapProxy.h b/lib/rmg/threadpool/MapProxy.h index c05530d50..a85d74fbf 100644 --- a/lib/rmg/threadpool/MapProxy.h +++ b/lib/rmg/threadpool/MapProxy.h @@ -26,6 +26,7 @@ public: void insertObject(CGObjectInstance * obj); void insertObjects(std::set& objects); + void removeObject(CGObjectInstance* obj); void drawTerrain(CRandomGenerator & generator, std::vector & tiles, TerrainId terrain); void drawRivers(CRandomGenerator & generator, std::vector & tiles, TerrainId terrain); From 626664b8f6ea0bd14a4959ae85c5088a8325cc0d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 12 Aug 2023 14:16:47 +0300 Subject: [PATCH 28/36] Fix possible thread race on accessing GuiHandler from server handler thread without acquiring lock --- client/CServerHandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 18e9ee444..667684b2b 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -88,6 +88,8 @@ template class CApplyOnLobby : public CBaseForLobbyApply public: bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const override { + boost::unique_lock un(*CPlayerInterface::pim); + T * ptr = static_cast(pack); ApplyOnLobbyHandlerNetPackVisitor visitor(*handler); From 1eabb738dcaa7c1c3bafc6366d62b426efb09387 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 12 Aug 2023 22:02:37 +0300 Subject: [PATCH 29/36] NKAI: fix heroes not recruited --- AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp index 581c725e6..8f9b80f6d 100644 --- a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp +++ b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp @@ -84,7 +84,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const } } - if(treasureSourcesCount < 5) + if(treasureSourcesCount < 5 && (town->garrisonHero || town->getUpperArmy()->getArmyStrength() < 10000)) continue; if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1 From a7859dae39ec37101f7d879d9a8ebab341403d44 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 12 Aug 2023 22:08:15 +0300 Subject: [PATCH 30/36] Battle AI: archangels cast again --- lib/battle/CBattleInfoCallback.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index f207c7170..ff44c2ba9 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1732,6 +1732,10 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(CRandomGenerator & rand,const TConstBonusListPtr bl = caster->getBonuses(Selector::type()(BonusType::SPELLCASTER)); if (!bl->size()) return SpellID::NONE; + + if(bl->size() == 1) + return SpellID(bl->front()->subtype); + int totalWeight = 0; for(const auto & b : *bl) { From 9509974a5d629018a9fe5e0dd977cac9041a197a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 12 Aug 2023 16:58:24 +0300 Subject: [PATCH 31/36] Updated changelog --- ChangeLog.md | 40 +++++++++++++++++++++++++++--- launcher/eu.vcmi.VCMI.metainfo.xml | 2 +- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 3ba074a01..584fd2319 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,10 +1,44 @@ # 1.3.0 -> 1.3.1 -(unreleased) -* Fixed crash on starting game with outdated mods -* Fixed Android mod manager crash +### GENERAL: * Fixed framerate drops on hero movement with active hota mod +* Fade-out animations will now be skipped when instant hero movement speed is used +* Restarting loaded campaing scenario will now correctly reapply starting bonus * Reverted FPS limit on mobile systems back to 60 fps +* Fixed loading of translations for maps and campaigns +* Fixed loading of preconfigured starting army for heroes with preconfigured spells +* Background battlefield obstacles will now appear below creatures +* it is now possible to load save game located inside mod +* Added option to configure reserved screen area in Launcher on iOS + +### AI PLAYER: +* BattleAI: Improved performance of AI spell selection +* NKAI: Fixed freeze on attempt to exchange army between garrisoned and visiting hero +* NKAI: Fixed town threat calculation +* VCAI: Added workaround to avoid freeze on attempting to reach unreachable location + +### RANDOM MAP GENERATOR: +* Fixed placement of roads inside rock in underground +* Fixed placement of shifted creature animations from HotA +* Fixed placement of treasures at the boundary of wide connections +* Added more potential locations for quest artifacts in zone + +### STABILITY: +* When starting client without H3 data game will now show message instead of silently crashing +* When starting invalid map in campaign, game will now show message instead of silently crashing +* Fixed crash on starting game with outdated mods +* Fixed crash on attempt to sacrifice all your artifacts in Altar of Sacrifice +* Fixed crash on leveling up after winning battle as defender +* Fixed possible crash on end of battle opening sound +* Fixed crash on accepting battle result after winning battle as defender +* Fixed possible crash on casting spell in battle by AI +* Fixed multiple possible crashes on managing mods on Android +* Fixed multiple possible crashes on importing data on Android +* Fixed crash on refusing rewards from town building +* Fixed possible crash on threat evaluation by NKAI +* Fixed crash on using haptic feedback on some Android systems +* Fixed crash on right-clicking flags area in RMG setup mode +* Fixed crash on opening Blacksmith window and Build Structure dialogs in some localizations # 1.2.1 -> 1.3.0 diff --git a/launcher/eu.vcmi.VCMI.metainfo.xml b/launcher/eu.vcmi.VCMI.metainfo.xml index f89354b39..bfb05414f 100644 --- a/launcher/eu.vcmi.VCMI.metainfo.xml +++ b/launcher/eu.vcmi.VCMI.metainfo.xml @@ -51,7 +51,7 @@ StrategyGame - + From 074fd42d842fe67345dcb418241d014ab566ce54 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 17 Aug 2023 10:20:46 +0300 Subject: [PATCH 32/36] Added missing includes --- Global.h | 2 ++ client/CServerHandler.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/Global.h b/Global.h index dda61c804..3c308f54c 100644 --- a/Global.h +++ b/Global.h @@ -118,6 +118,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); #include #include #include +#include #include #include #include @@ -126,6 +127,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); #include #include #include +#include #include //The only available version is 3, as of Boost 1.50 diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 667684b2b..7a21a5b84 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include "../lib/serializer/Cast.h" #include "LobbyClientNetPackVisitors.h" From bfa12080ac136b25cbf91f5366a11e1e2fadb854 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 17 Aug 2023 10:21:03 +0300 Subject: [PATCH 33/36] Added downloads counter for 1.3.1 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 18ca646d1..b635ac00c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![GitHub](https://github.com/vcmi/vcmi/actions/workflows/github.yml/badge.svg)](https://github.com/vcmi/vcmi/actions/workflows/github.yml) [![Coverity Scan Build Status](https://scan.coverity.com/projects/vcmi/badge.svg)](https://scan.coverity.com/projects/vcmi) [![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.3.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.3.0) +[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.3.1/total)](https://github.com/vcmi/vcmi/releases/tag/1.3.1) [![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases) # VCMI Project VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities. From 6e7b7932a0b56034549acf9162ee53db2e82d249 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 17 Aug 2023 10:25:31 +0300 Subject: [PATCH 34/36] Fixed recruitment of hero from towns located on map border --- server/HeroPoolProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/HeroPoolProcessor.cpp b/server/HeroPoolProcessor.cpp index 4c92e5963..f32a88be0 100644 --- a/server/HeroPoolProcessor.cpp +++ b/server/HeroPoolProcessor.cpp @@ -244,7 +244,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy hr.hid = recruitedHero->subID; hr.player = player; hr.tile = recruitedHero->convertFromVisitablePos(targetPos ); - if(gameHandler->getTile(hr.tile)->isWater() && !recruitedHero->boat) + if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->boat) { //Create a new boat for hero gameHandler->createObject(targetPos , Obj::BOAT, recruitedHero->getBoatType().getNum()); From 8b8d2b881094f9ac70eb6fc9aee9f650195883d4 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 17 Aug 2023 10:25:46 +0300 Subject: [PATCH 35/36] Updated changelog --- ChangeLog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 584fd2319..b3c964975 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -15,7 +15,9 @@ * BattleAI: Improved performance of AI spell selection * NKAI: Fixed freeze on attempt to exchange army between garrisoned and visiting hero * NKAI: Fixed town threat calculation +* NKAI: Fixed recruitment of new heroes * VCAI: Added workaround to avoid freeze on attempting to reach unreachable location +* VCAI: Fixed spellcasting by Archangels ### RANDOM MAP GENERATOR: * Fixed placement of roads inside rock in underground @@ -26,6 +28,7 @@ ### STABILITY: * When starting client without H3 data game will now show message instead of silently crashing * When starting invalid map in campaign, game will now show message instead of silently crashing +* Blocked loading of saves made with different set of mods to prevent crashes * Fixed crash on starting game with outdated mods * Fixed crash on attempt to sacrifice all your artifacts in Altar of Sacrifice * Fixed crash on leveling up after winning battle as defender @@ -39,6 +42,8 @@ * Fixed crash on using haptic feedback on some Android systems * Fixed crash on right-clicking flags area in RMG setup mode * Fixed crash on opening Blacksmith window and Build Structure dialogs in some localizations +* Fixed possible crash on displaying animated main menu +* Fixed crash on recruiting hero in town located on the border of map # 1.2.1 -> 1.3.0 From ec0a51bd5c37903d2be883fe4be61b280c1dcf1a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 17 Aug 2023 12:25:02 +0300 Subject: [PATCH 36/36] Fixed border scrolling when game window is maximized --- ChangeLog.md | 1 + client/adventureMap/AdventureMapInterface.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index b3c964975..31a09a49b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -10,6 +10,7 @@ * Background battlefield obstacles will now appear below creatures * it is now possible to load save game located inside mod * Added option to configure reserved screen area in Launcher on iOS +* Fixed border scrolling when game window is maximized ### AI PLAYER: * BattleAI: Improved performance of AI spell selection diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 45ea69a63..7a8d4b3ea 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -169,10 +169,10 @@ void AdventureMapInterface::tick(uint32_t msPassed) void AdventureMapInterface::handleMapScrollingUpdate(uint32_t timePassed) { /// Width of window border, in pixels, that triggers map scrolling - static constexpr uint32_t borderScrollWidth = 15; + static constexpr int32_t borderScrollWidth = 15; - uint32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float(); - uint32_t scrollDistance = scrollSpeedPixels * timePassed / 1000; + int32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float(); + int32_t scrollDistance = scrollSpeedPixels * timePassed / 1000; Point cursorPosition = GH.getCursorPosition(); Point scrollDirection;