From 7a096460091530330ed04387d22c9f4d5c086cf1 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 25 Oct 2023 21:56:00 +0300 Subject: [PATCH] Cleaned up dwelling randomization --- lib/gameState/CGameState.cpp | 83 +++++++++-------------------- lib/gameState/CGameState.h | 3 +- lib/mapObjects/CGCreature.cpp | 1 - lib/mapObjects/CGDwelling.cpp | 8 +++ lib/mapObjects/CGHeroInstance.cpp | 18 +++---- lib/mapObjects/CGHeroInstance.h | 1 - lib/mapObjects/CGObjectInstance.h | 7 ++- lib/mapObjects/CGTownInstance.cpp | 15 +++--- lib/mapObjects/CGTownInstance.h | 1 - lib/mapObjects/IObjectInterface.cpp | 3 ++ lib/mapping/MapFormatH3M.cpp | 7 +++ mapeditor/inspector/inspector.cpp | 27 +++------- 12 files changed, 69 insertions(+), 105 deletions(-) diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index bd88d0b03..b3aebe1b7 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -77,34 +77,6 @@ public: } }; -static CGObjectInstance * createObject(MapObjectID id, MapObjectSubID subid, const int3 & pos, const PlayerColor & owner) -{ - CGObjectInstance * nobj; - switch(id) - { - case Obj::HERO: - { - auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->objects[subid]->heroClass->getIndex()); - nobj = handler->create(handler->getTemplates().front()); - break; - } - case Obj::TOWN: - nobj = new CGTownInstance(); - break; - default: //rest of objects - nobj = new CGObjectInstance(); - break; - } - nobj->ID = id; - nobj->subID = subid; - nobj->pos = pos; - nobj->tempOwner = owner; - if (id != Obj::HERO) - nobj->appearance = VLC->objtypeh->getHandlerFor(id, subid)->getTemplates().front(); - - return nobj; -} - HeroTypeID CGameState::pickNextHeroType(const PlayerColor & owner) { const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner); @@ -547,44 +519,30 @@ void CGameState::initRandomFactionsForPlayers() void CGameState::randomizeMapObjects() { logGlobal->debug("\tRandomizing objects"); - for(CGObjectInstance *obj : map->objects) + for(CGObjectInstance *object : map->objects) { - if(!obj) + if(!object) continue; - { - std::pair ran = pickObject(obj); - if(ran.first == Obj::NO_OBJ || ran.second<0) //this is not a random object, or we couldn't find anything - { - if(obj->ID==Obj::TOWN || obj->ID==Obj::MONSTER) - obj->setType(obj->ID, obj->subID); // update def, if necessary - } - else if(ran.first==Obj::HERO)//special code for hero - { - auto * h = dynamic_cast(obj); - obj->setType(ran.first, ran.second); - map->heroesOnMap.emplace_back(h); - } - else if(ran.first==Obj::TOWN)//special code for town - { - auto * t = dynamic_cast(obj); - obj->setType(ran.first, ran.second); - map->towns.emplace_back(t); - } - else - { - obj->setType(ran.first, ran.second); - } - } + object->pickRandomObject(getRandomGenerator()); + + auto * hero = dynamic_cast(object); + auto * town = dynamic_cast(object); + + if (hero) + map->heroesOnMap.emplace_back(hero); + + if (town) + map->towns.emplace_back(town); //handle Favouring Winds - mark tiles under it - if(obj->ID == Obj::FAVORABLE_WINDS) + if(object->ID == Obj::FAVORABLE_WINDS) { - for (int i = 0; i < obj->getWidth() ; i++) + for (int i = 0; i < object->getWidth() ; i++) { - for (int j = 0; j < obj->getHeight() ; j++) + for (int j = 0; j < object->getHeight() ; j++) { - int3 pos = obj->pos - int3(i,j,0); + int3 pos = object->pos - int3(i,j,0); if(map->isInTheMap(pos)) map->getTile(pos).extTileFlags |= 128; } } @@ -617,7 +575,14 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy } } - CGObjectInstance * hero = createObject(Obj::HERO, heroTypeId, townPos, playerColor); + auto handler = VLC->objtypeh->getHandlerFor(Obj::HERO, VLC->heroh->objects[heroTypeId]->heroClass->getIndex()); + CGObjectInstance * hero = handler->create(handler->getTemplates().front()); + + hero->ID = Obj::HERO; + hero->subID = VLC->heroh->objects[heroTypeId]->heroClass->getIndex(); + hero->tempOwner = playerColor; + + hero->pos = townPos; hero->pos += hero->getVisitableOffset(); map->getEditManager()->insertObject(hero); } diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index a99076230..7b613c05d 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -115,6 +115,8 @@ public: void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; bool giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid); + /// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly + HeroTypeID pickNextHeroType(const PlayerColor & owner); void apply(CPack *pack); BattleField battleGetBattlefieldType(int3 tile, CRandomGenerator & rand); @@ -214,7 +216,6 @@ private: bool isUsedHero(const HeroTypeID & hid) const; //looks in heroes and prisons std::set getUnusedAllowedHeroes(bool alsoIncludeNotAllowed = false) const; HeroTypeID pickUnusedHeroTypeRandomly(const PlayerColor & owner); // picks a unused hero type randomly - HeroTypeID pickNextHeroType(const PlayerColor & owner); // picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly UpgradeInfo fillUpgradeInfo(const CStackInstance &stack) const; // ---- data ----- diff --git a/lib/mapObjects/CGCreature.cpp b/lib/mapObjects/CGCreature.cpp index a4f359b3e..cab0a1523 100644 --- a/lib/mapObjects/CGCreature.cpp +++ b/lib/mapObjects/CGCreature.cpp @@ -152,7 +152,6 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const break; } } - } CreatureID CGCreature::getCreature() const diff --git a/lib/mapObjects/CGDwelling.cpp b/lib/mapObjects/CGDwelling.cpp index a911e3089..ff8e73886 100644 --- a/lib/mapObjects/CGDwelling.cpp +++ b/lib/mapObjects/CGDwelling.cpp @@ -48,6 +48,10 @@ CGDwelling::~CGDwelling() = default; FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand) { + if (ID == Obj::RANDOM_DWELLING_FACTION) + return FactionID(subID); + + assert(ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL); assert(randomizationInfo.has_value()); if (!randomizationInfo) return FactionID::CASTLE; @@ -102,6 +106,10 @@ FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand) int CGDwelling::randomizeLevel(CRandomGenerator & rand) { + if (ID == Obj::RANDOM_DWELLING_LVL) + return subID.getNum(); + + assert(ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_FACTION); assert(randomizationInfo.has_value()); if (!randomizationInfo) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 673d91684..c625e7c95 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -296,16 +296,6 @@ void CGHeroInstance::initHero(CRandomGenerator & rand, const HeroTypeID & SUBID) initHero(rand); } -void CGHeroInstance::setType(si32 ID, si32 subID) -{ - assert(ID == Obj::HERO); // just in case - type = VLC->heroh->objects[subID]; - - CGObjectInstance::setType(ID, type->heroClass->getIndex()); // to find object handler we must use heroClass->id - this->subID = subID; // after setType subID used to store unique hero identify id. Check issue 2277 for details - randomizeArmy(type->heroClass->faction); -} - void CGHeroInstance::initHero(CRandomGenerator & rand) { assert(validTypes(true)); @@ -580,6 +570,14 @@ void CGHeroInstance::pickRandomObject(CRandomGenerator & rand) ID = Obj::HERO; subID = cb->gameState()->pickNextHeroType(getOwner()); } + type = VLC->heroh->objects[subID]; + + // to find object handler we must use heroClass->id + // after setType subID used to store unique hero identify id. Check issue 2277 for details + setType(ID, type->heroClass->getIndex()); + this->subID = subID; + + randomizeArmy(type->heroClass->faction); } void CGHeroInstance::initObj(CRandomGenerator & rand) diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 492495d42..0c141d1e0 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -232,7 +232,6 @@ public: ////////////////////////////////////////////////////////////////////////// HeroTypeID getHeroType() const; - void setType(si32 ID, si32 subID) override; void initHero(CRandomGenerator & rand); void initHero(CRandomGenerator & rand, const HeroTypeID & SUBID); diff --git a/lib/mapObjects/CGObjectInstance.h b/lib/mapObjects/CGObjectInstance.h index d56427e3b..d235694b6 100644 --- a/lib/mapObjects/CGObjectInstance.h +++ b/lib/mapObjects/CGObjectInstance.h @@ -106,10 +106,6 @@ public: virtual int getSightRadius() const; /// returns (x,y,0) offset to a visitable tile of object virtual int3 getVisitableOffset() const; - /// Called mostly during map randomization to turn random object into a regular one (e.g. "Random Monster" into "Pikeman") - virtual void setType(si32 ID, si32 subID); - - /// returns text visible in status bar with specific hero/player active. /// Returns generic name of object, without any player-specific info virtual std::string getObjectName() const; @@ -160,6 +156,9 @@ protected: /// virtual method that allows synchronously update object state on server and all clients virtual void setPropertyDer(ui8 what, ui32 val); + /// Called mostly during map randomization to turn random object into a regular one (e.g. "Random Monster" into "Pikeman") + void setType(si32 ID, si32 subID); + /// Gives dummy bonus from this object to hero. Can be used to track visited state void giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration::Type duration = BonusDuration::ONE_DAY) const; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 5b66236d1..b16d548f7 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -486,6 +486,12 @@ void CGTownInstance::pickRandomObject(CRandomGenerator & rand) ID = MapObjectID::TOWN; subID = randomizeFaction(rand); } + + assert(ID == Obj::TOWN); // just in case + setType(ID, subID); + town = (*VLC->townh)[subID]->town; + randomizeArmy(subID); + updateAppearance(); } void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structures @@ -779,15 +785,6 @@ std::vector CGTownInstance::availableItemsIds(EMarketMode mode) const return IMarket::availableItemsIds(mode); } -void CGTownInstance::setType(si32 ID, si32 subID) -{ - assert(ID == Obj::TOWN); // just in case - CGObjectInstance::setType(ID, subID); - town = (*VLC->townh)[subID]->town; - randomizeArmy(subID); - updateAppearance(); -} - void CGTownInstance::updateAppearance() { auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId(); diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 18a6e8eb2..2a197ec55 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -141,7 +141,6 @@ public: bool allowsTrade(EMarketMode mode) const override; std::vector availableItemsIds(EMarketMode mode) const override; - void setType(si32 ID, si32 subID) override; void updateAppearance(); ////////////////////////////////////////////////////////////////////////// diff --git a/lib/mapObjects/IObjectInterface.cpp b/lib/mapObjects/IObjectInterface.cpp index 268c99c4c..b0e0b11cd 100644 --- a/lib/mapObjects/IObjectInterface.cpp +++ b/lib/mapObjects/IObjectInterface.cpp @@ -46,6 +46,9 @@ void IObjectInterface::newTurn(CRandomGenerator & rand) const void IObjectInterface::initObj(CRandomGenerator & rand) {} +void IObjectInterface::pickRandomObject(CRandomGenerator & rand) +{} + void IObjectInterface::setProperty( ui8 what, ui32 val ) {} diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index a94fbc262..6bb12268b 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1336,12 +1336,19 @@ CGObjectInstance * CMapLoaderH3M::readDwellingRandom(const int3 & mapPosition, s if(object->randomizationInfo->identifier != 0) reader->readBitmaskFactions(object->randomizationInfo->allowedFactions, false); } + else + object->randomizationInfo->allowedFactions.insert(FactionID(objectTemplate->subid)); if(hasLevelInfo) { object->randomizationInfo->minLevel = std::max(reader->readUInt8(), static_cast(0)) + 1; object->randomizationInfo->maxLevel = std::min(reader->readUInt8(), static_cast(6)) + 1; } + else + { + object->randomizationInfo->minLevel = objectTemplate->subid; + object->randomizationInfo->maxLevel = objectTemplate->subid; + } return object; } diff --git a/mapeditor/inspector/inspector.cpp b/mapeditor/inspector/inspector.cpp index fe81dd27e..459a7daa4 100644 --- a/mapeditor/inspector/inspector.cpp +++ b/mapeditor/inspector/inspector.cpp @@ -87,14 +87,6 @@ void Initializer::initialize(CGDwelling * o) if(!o) return; o->tempOwner = defaultPlayer; - - switch(o->ID) - { - case Obj::RANDOM_DWELLING: - case Obj::RANDOM_DWELLING_LVL: - case Obj::RANDOM_DWELLING_FACTION: - o->initRandomObjectInfo(); - } } void Initializer::initialize(CGGarrison * o) @@ -243,11 +235,8 @@ void Inspector::updateProperties(CGDwelling * o) addProperty("Owner", o->tempOwner, false); - if(dynamic_cast(o->info)) - { - auto * delegate = new PickObjectDelegate(controller, PickObjectDelegate::typedFilter); - addProperty("Same as town", PropertyEditorPlaceholder(), delegate, false); - } + auto * delegate = new PickObjectDelegate(controller, PickObjectDelegate::typedFilter); + addProperty("Same as town", PropertyEditorPlaceholder(), delegate, false); } void Inspector::updateProperties(CGLighthouse * o) @@ -641,12 +630,12 @@ void Inspector::setProperty(CGDwelling * o, const QString & key, const QVariant if(key == "Same as town") { - if(auto * info = dynamic_cast(o->info)) - { - info->instanceId = ""; - if(CGTownInstance * town = data_cast(value.toLongLong())) - info->instanceId = town->instanceName; - } + if (!o->randomizationInfo.has_value()) + o->randomizationInfo = CGDwellingRandomizationInfo(); + + o->randomizationInfo->instanceId = ""; + if(CGTownInstance * town = data_cast(value.toLongLong())) + o->randomizationInfo->instanceId = town->instanceName; } }