diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index b3986de94..0f7068402 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); } @@ -494,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/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/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 diff --git a/ChangeLog.md b/ChangeLog.md index 24edce517..31a09a49b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,51 @@ +# 1.3.0 -> 1.3.1 + +### 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 +* Fixed border scrolling when game window is maximized + +### 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 +* 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 +* 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 +* 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 +* 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 +* 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 ### GENERAL: 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/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. diff --git a/android/vcmi-app/build.gradle b/android/vcmi-app/build.gradle index 34c9dcc70..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 1306 - versionName "1.3.0" + versionCode 1310 + versionName "1.3.1" setProperty("archivesBaseName", "vcmi") } 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" 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/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 0f7ef98e2..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; @@ -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(); } 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/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); }); - - } } 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) ); 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); 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/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); 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..bfb05414f 100644 --- a/launcher/eu.vcmi.VCMI.metainfo.xml +++ b/launcher/eu.vcmi.VCMI.metainfo.xml @@ -51,6 +51,7 @@ StrategyGame + 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..186689a6c 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 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 Встановити 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_queuewarn("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; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 6bb44ca41..735737da5 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -335,7 +335,7 @@ struct DLL_LINKAGE SetAvailableHero : public CPackForClient { SetAvailableHero() { - army.clear(); + army.clearSlots(); } void applyGs(CGameState * gs); diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index db8a2dd8d..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,6 +2273,8 @@ void BattleAttack::applyGs(CGameState * gs) void StartAction::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE + CStack *st = gs->curB->getStack(ba.stackNumber); if(ba.actionType == EActionType::END_TACTIC_PHASE) @@ -2313,7 +2323,7 @@ void StartAction::applyGs(CGameState *gs) void BattleSpellCast::applyGs(CGameState * gs) const { - assert(gs->curB); + THROW_IF_NO_BATTLE if(castByHero) { @@ -2326,6 +2336,7 @@ void BattleSpellCast::applyGs(CGameState * gs) const void SetStackEffect::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2344,6 +2355,7 @@ void SetStackEffect::applyBattle(IBattleState * battleState) void StacksInjured::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2355,6 +2367,7 @@ void StacksInjured::applyBattle(IBattleState * battleState) void BattleUnitsChanged::applyGs(CGameState *gs) { + THROW_IF_NO_BATTLE applyBattle(gs->curB); } @@ -2385,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) @@ -2417,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) @@ -2444,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) { 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) { diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 2b91f39e8..b2c1d3261 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -58,7 +58,7 @@ std::string CBank::getHoverText(PlayerColor player) const void CBank::setConfig(const BankConfig & config) { bc = std::make_unique(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/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 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); } 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/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..d1afd557e 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); @@ -433,6 +452,28 @@ bool ObjectManager::createRequiredObjects() void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/) { + //object.finalize(map); + + if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER) + { + //Fix for HoTA offset - lonely guards + + 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; + + //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); + } object.finalize(map); Zone::Lock lock(zone.areaMutex); @@ -443,8 +484,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 +542,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 +671,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; } 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/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); } 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); 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/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/CGameHandler.cpp b/server/CGameHandler.cpp index 72b6e2438..db3162a27 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2943,7 +2943,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; 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)); } diff --git a/server/HeroPoolProcessor.cpp b/server/HeroPoolProcessor.cpp index 2d50595b0..f32a88be0 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); } } @@ -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()); 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 *());