diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index bcf8c1979..2709974d3 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -156,6 +156,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/rmg/modificators/ObjectDistributor.cpp ${MAIN_LIB_DIR}/rmg/modificators/RoadPlacer.cpp ${MAIN_LIB_DIR}/rmg/modificators/TreasurePlacer.cpp + ${MAIN_LIB_DIR}/rmg/modificators/PrisonHeroPlacer.cpp ${MAIN_LIB_DIR}/rmg/modificators/QuestArtifactPlacer.cpp ${MAIN_LIB_DIR}/rmg/modificators/ConnectionsPlacer.cpp ${MAIN_LIB_DIR}/rmg/modificators/WaterAdopter.cpp @@ -526,6 +527,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/rmg/modificators/ObjectDistributor.h ${MAIN_LIB_DIR}/rmg/modificators/RoadPlacer.h ${MAIN_LIB_DIR}/rmg/modificators/TreasurePlacer.h + ${MAIN_LIB_DIR}/rmg/modificators/PrisonHeroPlacer.h ${MAIN_LIB_DIR}/rmg/modificators/QuestArtifactPlacer.h ${MAIN_LIB_DIR}/rmg/modificators/ConnectionsPlacer.h ${MAIN_LIB_DIR}/rmg/modificators/WaterAdopter.h diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index c825899e2..58721b5d0 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -649,9 +649,18 @@ void CMap::banWaterHeroes() void CMap::banHero(const HeroTypeID & id) { + if (!vstd::contains(allowedHeroes, id)) + logGlobal->warn("Attempt to ban hero %s, who is already not allowed", id.encode(id)); allowedHeroes.erase(id); } +void CMap::unbanHero(const HeroTypeID & id) +{ + if (vstd::contains(allowedHeroes, id)) + logGlobal->warn("Attempt to unban hero %s, who is already allowed", id.encode(id)); + allowedHeroes.insert(id); +} + void CMap::initTerrain() { terrain.resize(boost::extents[levels()][width][height]); diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 7922df144..051d991aa 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -112,6 +112,7 @@ public: void banWaterArtifacts(); void banWaterHeroes(); void banHero(const HeroTypeID& id); + void unbanHero(const HeroTypeID & id); void banWaterSpells(); void banWaterSkills(); void banWaterContent(); diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 39ab6baed..070e507c2 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -35,7 +35,7 @@ VCMI_LIB_NAMESPACE_BEGIN CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) : mapGenOptions(mapGenOptions), randomSeed(RandomSeed), - allowedPrisons(0), monolithIndex(0) + monolithIndex(0) { loadConfig(); rand.setSeed(this->randomSeed); @@ -96,12 +96,6 @@ const CMapGenOptions& CMapGenerator::getMapGenOptions() const return mapGenOptions; } -void CMapGenerator::initPrisonsRemaining() -{ - allowedPrisons = map->getMap(this).allowedHeroes.size(); - allowedPrisons = std::max (0, allowedPrisons - 16 * mapGenOptions.getHumanOrCpuPlayerCount()); //so at least 16 heroes will be available for every player -} - void CMapGenerator::initQuestArtsRemaining() { //TODO: Move to QuestArtifactPlacer? @@ -122,7 +116,6 @@ std::unique_ptr CMapGenerator::generate() addHeaderInfo(); map->initTiles(*this, rand); Load::Progress::step(); - initPrisonsRemaining(); initQuestArtsRemaining(); genZones(); Load::Progress::step(); @@ -468,11 +461,6 @@ int CMapGenerator::getNextMonlithIndex() } } -int CMapGenerator::getPrisonsRemaning() const -{ - return allowedPrisons; -} - std::shared_ptr CMapGenerator::getZonePlacer() const { return placer; @@ -488,6 +476,7 @@ const std::vector CMapGenerator::getAllPossibleHeroes() const auto isWaterMap = map->getMap(this).isWaterMap(); //Skip heroes that were banned, including the ones placed in prisons std::vector ret; + for (HeroTypeID hero : map->getMap(this).allowedHeroes) { auto * h = dynamic_cast(VLC->heroTypes()->getById(hero)); @@ -517,14 +506,12 @@ const std::vector CMapGenerator::getAllPossibleHeroes() const void CMapGenerator::banQuestArt(const ArtifactID & id) { - //TODO: Protect with mutex map->getMap(this).allowedArtifact.erase(id); } -void CMapGenerator::banHero(const HeroTypeID & id) +void CMapGenerator::unbanQuestArt(const ArtifactID & id) { - //TODO: Protect with mutex - map->getMap(this).banHero(id); + map->getMap(this).allowedArtifact.insert(id); } Zone * CMapGenerator::getZoneWater() const diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index dd18108c5..372704d93 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -65,8 +65,7 @@ public: const std::vector & getAllPossibleQuestArtifacts() const; const std::vector getAllPossibleHeroes() const; void banQuestArt(const ArtifactID & id); - void banHero(const HeroTypeID& id); - + void unbanQuestArt(const ArtifactID & id); Zone * getZoneWater() const; void addWaterTreasuresInfo(); @@ -82,7 +81,6 @@ private: std::vector connectionsLeft; - int allowedPrisons; int monolithIndex; std::vector questArtifacts; diff --git a/lib/rmg/RmgMap.cpp b/lib/rmg/RmgMap.cpp index c7eb58581..749e0efdb 100644 --- a/lib/rmg/RmgMap.cpp +++ b/lib/rmg/RmgMap.cpp @@ -19,6 +19,7 @@ #include "modificators/ObjectManager.h" #include "modificators/RoadPlacer.h" #include "modificators/TreasurePlacer.h" +#include "modificators/PrisonHeroPlacer.h" #include "modificators/QuestArtifactPlacer.h" #include "modificators/ConnectionsPlacer.h" #include "modificators/TownPlacer.h" @@ -127,6 +128,7 @@ void RmgMap::initTiles(CMapGenerator & generator, CRandomGenerator & rand) void RmgMap::addModificators() { bool hasObjectDistributor = false; + bool hasHeroPlacer = false; bool hasRockFiller = false; for(auto & z : getZones()) @@ -139,6 +141,11 @@ void RmgMap::addModificators() zone->addModificator(); hasObjectDistributor = true; } + if (!hasHeroPlacer) + { + zone->addModificator(); + hasHeroPlacer = true; + } zone->addModificator(); zone->addModificator(); zone->addModificator(); diff --git a/lib/rmg/modificators/ObjectDistributor.cpp b/lib/rmg/modificators/ObjectDistributor.cpp index 5e9eacca8..89910c33f 100644 --- a/lib/rmg/modificators/ObjectDistributor.cpp +++ b/lib/rmg/modificators/ObjectDistributor.cpp @@ -15,6 +15,7 @@ #include "../RmgMap.h" #include "../CMapGenerator.h" #include "TreasurePlacer.h" +#include "PrisonHeroPlacer.h" #include "QuestArtifactPlacer.h" #include "TownPlacer.h" #include "TerrainPainter.h" @@ -75,7 +76,6 @@ void ObjectDistributor::distributeLimitedObjects() auto rmgInfo = handler->getRMGInfo(); - // FIXME: Random order of distribution RandomGeneratorUtil::randomShuffle(matchingZones, zone.getRand()); for (auto& zone : matchingZones) { @@ -146,7 +146,18 @@ void ObjectDistributor::distributePrisons() RandomGeneratorUtil::randomShuffle(zones, zone.getRand()); - size_t allowedPrisons = generator.getPrisonsRemaning(); + // TODO: Some shorthand for unique Modificator + PrisonHeroPlacer * prisonHeroPlacer = nullptr; + for(auto & z : map.getZones()) + { + prisonHeroPlacer = z.second->getModificator(); + if (prisonHeroPlacer) + { + break; + } + } + + size_t allowedPrisons = prisonHeroPlacer->getPrisonsRemaning(); for (int i = zones.size() - 1; i >= 0; i--) { auto zone = zones[i].second; diff --git a/lib/rmg/modificators/PrisonHeroPlacer.cpp b/lib/rmg/modificators/PrisonHeroPlacer.cpp new file mode 100644 index 000000000..d4787784d --- /dev/null +++ b/lib/rmg/modificators/PrisonHeroPlacer.cpp @@ -0,0 +1,73 @@ +/* +* PrisonHeroPlacer.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#include "StdInc.h" +#include "PrisonHeroPlacer.h" +#include "../CMapGenerator.h" +#include "../RmgMap.h" +#include "TreasurePlacer.h" +#include "../CZonePlacer.h" +#include "../../VCMI_Lib.h" +#include "../../mapObjectConstructors/AObjectTypeHandler.h" +#include "../../mapObjectConstructors/CObjectClassesHandler.h" +#include "../../mapObjects/MapObjects.h" + +VCMI_LIB_NAMESPACE_BEGIN + +void PrisonHeroPlacer::process() +{ + getAllowedHeroes(); +} + +void PrisonHeroPlacer::init() +{ + // Reserve at least 16 heroes for each player + reservedHeroes = 16 * generator.getMapGenOptions().getHumanOrCpuPlayerCount(); +} + +void PrisonHeroPlacer::getAllowedHeroes() +{ + // TODO: Give each zone unique HeroPlacer with private hero list? + + // Call that only once + if (allowedHeroes.empty()) + { + allowedHeroes = generator.getAllPossibleHeroes(); + } +} + +int PrisonHeroPlacer::getPrisonsRemaning() const +{ + return std::max(allowedHeroes.size() - reservedHeroes, 0); +} + +HeroTypeID PrisonHeroPlacer::drawRandomHero() +{ + RecursiveLock lock(externalAccessMutex); + if (getPrisonsRemaning() > 0) + { + RandomGeneratorUtil::randomShuffle(allowedHeroes, zone.getRand()); + HeroTypeID ret = allowedHeroes.back(); + allowedHeroes.pop_back(); + return ret; + } + else + { + throw rmgException("No unused heroes left for prisons!"); + } +} + +void PrisonHeroPlacer::restoreDrawnHero(const HeroTypeID & hid) +{ + RecursiveLock lock(externalAccessMutex); + allowedHeroes.push_back(hid); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/modificators/PrisonHeroPlacer.h b/lib/rmg/modificators/PrisonHeroPlacer.h new file mode 100644 index 000000000..62c32d381 --- /dev/null +++ b/lib/rmg/modificators/PrisonHeroPlacer.h @@ -0,0 +1,41 @@ +/* +* PrisonHeroPlacer, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#pragma once +#include "../Zone.h" +#include "../Functions.h" +#include "../../mapObjects/ObjectTemplate.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CRandomGenerator; + +class PrisonHeroPlacer : public Modificator +{ +public: + MODIFICATOR(PrisonHeroPlacer); + + void process() override; + void init() override; + + int getPrisonsRemaning() const; + [[nodiscard]] HeroTypeID drawRandomHero(); + void restoreDrawnHero(const HeroTypeID & hid); + +private: + void getAllowedHeroes(); + size_t reservedHeroes; + +protected: + + std::vector allowedHeroes; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/modificators/QuestArtifactPlacer.cpp b/lib/rmg/modificators/QuestArtifactPlacer.cpp index fd9fe44d8..17a181716 100644 --- a/lib/rmg/modificators/QuestArtifactPlacer.cpp +++ b/lib/rmg/modificators/QuestArtifactPlacer.cpp @@ -40,11 +40,18 @@ void QuestArtifactPlacer::addQuestArtZone(std::shared_ptr otherZone) void QuestArtifactPlacer::addQuestArtifact(const ArtifactID& id) { + logGlobal->info("Need to place quest artifact %s", VLC->artifacts()->getById(id)->getNameTranslated()); RecursiveLock lock(externalAccessMutex); - logGlobal->info("Need to place quest artifact artifact %s", VLC->artifacts()->getById(id)->getNameTranslated()); questArtifactsToPlace.emplace_back(id); } +void QuestArtifactPlacer::removeQuestArtifact(const ArtifactID& id) +{ + logGlobal->info("Will not try to place quest artifact %s", VLC->artifacts()->getById(id)->getNameTranslated()); + RecursiveLock lock(externalAccessMutex); + vstd::erase_if_present(questArtifactsToPlace, id); +} + void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj) { RecursiveLock lock(externalAccessMutex); @@ -131,9 +138,10 @@ ArtifactID QuestArtifactPlacer::drawRandomArtifact() RecursiveLock lock(externalAccessMutex); if (!questArtifacts.empty()) { + RandomGeneratorUtil::randomShuffle(questArtifacts, zone.getRand()); ArtifactID ret = questArtifacts.back(); questArtifacts.pop_back(); - RandomGeneratorUtil::randomShuffle(questArtifacts, zone.getRand()); + generator.banQuestArt(ret); return ret; } else @@ -142,10 +150,11 @@ ArtifactID QuestArtifactPlacer::drawRandomArtifact() } } -void QuestArtifactPlacer::addRandomArtifact(ArtifactID artid) +void QuestArtifactPlacer::addRandomArtifact(const ArtifactID & artid) { RecursiveLock lock(externalAccessMutex); questArtifacts.push_back(artid); + generator.unbanQuestArt(artid); } VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/modificators/QuestArtifactPlacer.h b/lib/rmg/modificators/QuestArtifactPlacer.h index fb46c8de6..b5b9f5987 100644 --- a/lib/rmg/modificators/QuestArtifactPlacer.h +++ b/lib/rmg/modificators/QuestArtifactPlacer.h @@ -29,14 +29,15 @@ public: void findZonesForQuestArts(); void addQuestArtifact(const ArtifactID& id); + void removeQuestArtifact(const ArtifactID& id); void rememberPotentialArtifactToReplace(CGObjectInstance* obj); std::vector getPossibleArtifactsToReplace() const; void placeQuestArtifacts(CRandomGenerator & rand); void dropReplacedArtifact(CGObjectInstance* obj); size_t getMaxQuestArtifactCount() const; - ArtifactID drawRandomArtifact(); - void addRandomArtifact(ArtifactID artid); + [[nodiscard]] ArtifactID drawRandomArtifact(); + void addRandomArtifact(const ArtifactID & artid); protected: diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index f3b4a4c17..20c12e2eb 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -18,6 +18,7 @@ #include "../RmgMap.h" #include "../TileInfo.h" #include "../CZonePlacer.h" +#include "PrisonHeroPlacer.h" #include "QuestArtifactPlacer.h" #include "../../ArtifactUtils.h" #include "../../mapObjectConstructors/AObjectTypeHandler.h" @@ -32,6 +33,12 @@ VCMI_LIB_NAMESPACE_BEGIN +ObjectInfo::ObjectInfo(): + destroyObject([](){}) +{ + +} + void TreasurePlacer::process() { addAllPossibleObjects(); @@ -45,6 +52,7 @@ void TreasurePlacer::init() maxPrisons = 0; //Should be in the constructor, but we use macro for that DEPENDENCY(ObjectManager); DEPENDENCY(ConnectionsPlacer); + DEPENDENCY_ALL(PrisonHeroPlacer); POSTFUNCTION(RoadPlacer); } @@ -90,6 +98,16 @@ void TreasurePlacer::addAllPossibleObjects() auto prisonTemplates = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0)->getTemplates(zone.getTerrainType()); if (!prisonTemplates.empty()) { + PrisonHeroPlacer * prisonHeroPlacer = nullptr; + for(auto & z : map.getZones()) + { + prisonHeroPlacer = z.second->getModificator(); + if (prisonHeroPlacer) + { + break; + } + } + //prisons //levels 1, 5, 10, 20, 30 static int prisonsLevels = std::min(generator.getConfig().prisonExperience.size(), generator.getConfig().prisonValues.size()); @@ -97,16 +115,22 @@ void TreasurePlacer::addAllPossibleObjects() size_t prisonsLeft = getMaxPrisons(); for (int i = prisonsLevels - 1; i >= 0; i--) { + ObjectInfo oi; // Create new instance which will hold destructor operation + oi.value = generator.getConfig().prisonValues[i]; if (oi.value > zone.getMaxTreasureValue()) { continue; } - oi.generateObject = [i, this]() -> CGObjectInstance* + oi.generateObject = [i, this, prisonHeroPlacer, &oi]() -> CGObjectInstance* { - auto possibleHeroes = generator.getAllPossibleHeroes(); - HeroTypeID hid = *RandomGeneratorUtil::nextItem(possibleHeroes, zone.getRand()); + HeroTypeID hid = prisonHeroPlacer->drawRandomHero(); + oi.destroyObject = [hid, prisonHeroPlacer]() + { + // Hero can be used again + prisonHeroPlacer->restoreDrawnHero(hid); + }; auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0); auto* obj = dynamic_cast(factory->create()); @@ -114,7 +138,6 @@ void TreasurePlacer::addAllPossibleObjects() obj->setHeroType(hid); //will be initialized later obj->exp = generator.getConfig().prisonExperience[i]; obj->setOwner(PlayerColor::NEUTRAL); - generator.banHero(hid); return obj; }; @@ -441,6 +464,19 @@ void TreasurePlacer::addAllPossibleObjects() RandomGeneratorUtil::randomShuffle(creatures, zone.getRand()); + auto setRandomArtifact = [qap, &oi](CGSeerHut * obj) + { + ArtifactID artid = qap->drawRandomArtifact(); + oi.destroyObject = [artid, qap]() + { + // Artifact can be used again + qap->addRandomArtifact(artid); + qap->removeQuestArtifact(artid); + }; + obj->quest->mission.artifacts.push_back(artid); + qap->addQuestArtifact(artid); + }; + for(int i = 0; i < static_cast(creatures.size()); i++) { auto * creature = creatures[i]; @@ -451,7 +487,8 @@ void TreasurePlacer::addAllPossibleObjects() int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType()); - oi.generateObject = [creature, creaturesAmount, randomAppearance, this, qap]() -> CGObjectInstance * + // FIXME: Remove duplicated code for gold, exp and creaure reward + oi.generateObject = [creature, creaturesAmount, randomAppearance, setRandomArtifact]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); auto * obj = dynamic_cast(factory->create()); @@ -461,11 +498,7 @@ void TreasurePlacer::addAllPossibleObjects() reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; obj->configuration.info.push_back(reward); - ArtifactID artid = qap->drawRandomArtifact(); - obj->quest->mission.artifacts.push_back(artid); - - generator.banQuestArt(artid); - zone.getModificator()->addQuestArtifact(artid); + setRandomArtifact(obj); return obj; }; @@ -499,7 +532,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.probability = 10; oi.maxPerZone = 1; - oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance * + oi.generateObject = [i, randomAppearance, this, setRandomArtifact]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); auto * obj = dynamic_cast(factory->create()); @@ -508,20 +541,16 @@ void TreasurePlacer::addAllPossibleObjects() reward.reward.heroExperience = generator.getConfig().questRewardValues[i]; reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; obj->configuration.info.push_back(reward); - - ArtifactID artid = qap->drawRandomArtifact(); - obj->quest->mission.artifacts.push_back(artid); - - generator.banQuestArt(artid); - zone.getModificator()->addQuestArtifact(artid); - + + setRandomArtifact(obj); + return obj; }; if(!oi.templates.empty()) possibleSeerHuts.push_back(oi); - oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance * + oi.generateObject = [i, randomAppearance, this, setRandomArtifact]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); auto * obj = dynamic_cast(factory->create()); @@ -531,11 +560,7 @@ void TreasurePlacer::addAllPossibleObjects() reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; obj->configuration.info.push_back(reward); - ArtifactID artid = qap->drawRandomArtifact(); - obj->quest->mission.artifacts.push_back(artid); - - generator.banQuestArt(artid); - zone.getModificator()->addQuestArtifact(artid); + setRandomArtifact(obj); return obj; }; @@ -641,8 +666,14 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector } auto * object = oi->generateObject(); + if(oi->templates.empty()) + { + logGlobal->warn("Deleting randomized object with no templates: %s", object->getObjectName()); + oi->destroyObject(); + delete object; continue; + } auto templates = object->getObjectHandler()->getMostSpecificTemplates(zone.getTerrainType()); @@ -721,7 +752,7 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector instanceAccessibleArea.add(instance.getVisitablePosition()); } - //first object is good + //Do not clean up after first object if(rmgObject.instances().size() == 1) break; @@ -800,10 +831,10 @@ void TreasurePlacer::createTreasures(ObjectManager& manager) { for (auto* oi : treasurePile) { + oi->destroyObject(); oi->maxPerZone++; } }; - //place biggest treasures first at large distance, place smaller ones inbetween auto treasureInfo = zone.getTreasureInfo(); boost::sort(treasureInfo, valueComparator); diff --git a/lib/rmg/modificators/TreasurePlacer.h b/lib/rmg/modificators/TreasurePlacer.h index 88d10f5a7..ec87bfe8d 100644 --- a/lib/rmg/modificators/TreasurePlacer.h +++ b/lib/rmg/modificators/TreasurePlacer.h @@ -22,12 +22,15 @@ class CRandomGenerator; struct ObjectInfo { + ObjectInfo(); + std::vector> templates; ui32 value = 0; ui16 probability = 0; ui32 maxPerZone = 1; //ui32 maxPerMap; //unused std::function generateObject; + std::function destroyObject; void setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrain); }; diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index 00dcaad1c..8af35854b 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -131,7 +131,12 @@ void MapController::repairMap(CMap * map) const //fix hero instance if(auto * nih = dynamic_cast(obj.get())) { + // All heroes present on map or in prisons need to be allowed to rehire them after they are defeated + + // FIXME: How about custom scenarios where defeated hero cannot be hired again? + map->allowedHeroes.insert(nih->getHeroType()); + auto type = VLC->heroh->objects[nih->subID]; assert(type->heroClass); //TODO: find a way to get proper type name @@ -198,8 +203,6 @@ void MapController::repairMap(CMap * map) const auto a = ArtifactUtils::createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault())); art->storedArtifact = a; } - else - map->allowedArtifact.insert(art->getArtifact()); } } }