From c4cf2a100b4e29e5a691759fd85d70fd693fa5ac Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 2 May 2023 04:44:09 +0400 Subject: [PATCH] Fix behavior for hero bought in town tavern --- lib/CTownHandler.cpp | 1 + lib/mapObjects/CGTownBuilding.cpp | 57 +++++++++--- lib/mapObjects/CGTownBuilding.h | 12 ++- lib/mapObjects/CGTownInstance.cpp | 141 ++++++++++++++---------------- lib/mapObjects/CGTownInstance.h | 2 +- server/CGameHandler.cpp | 5 +- 6 files changed, 128 insertions(+), 90 deletions(-) diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index a6932254e..f8be58a7d 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -579,6 +579,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons auto * ret = new CBuilding(); ret->bid = getMappedValue(stringID, BuildingID::NONE, MappedKeys::BUILDING_NAMES_TO_TYPES, false); + ret->subId = BuildingSubID::NONE; if(ret->bid == BuildingID::NONE && !source["id"].isNull()) { diff --git a/lib/mapObjects/CGTownBuilding.cpp b/lib/mapObjects/CGTownBuilding.cpp index 5a578680b..3bf11aa96 100644 --- a/lib/mapObjects/CGTownBuilding.cpp +++ b/lib/mapObjects/CGTownBuilding.cpp @@ -14,6 +14,7 @@ #include "../CGeneralTextHandler.h" #include "../NetPacks.h" #include "../IGameCallback.h" +#include "../CGameState.h" VCMI_LIB_NAMESPACE_BEGIN @@ -284,12 +285,47 @@ void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) con town->addHeroToStructureVisitors(h, indexOnTV); } -CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown) +CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown, CRandomGenerator & rand) { bID = index; bType = subId; town = cgTown; indexOnTV = static_cast(town->bonusingBuildings.size()); + initObj(rand); +} + +void CTownRewardableBuilding::initObj(CRandomGenerator & rand) +{ + assert(town && town->town); + town->town->buildings.at(bID)->rewardableObjectInfo.configureObject(configuration, rand); + for(auto & rewardInfo : configuration.info) + { + for (auto & bonus : rewardInfo.reward.bonuses) + { + bonus.source = Bonus::TOWN_STRUCTURE; + bonus.sid = bID; + if (bonus.type == Bonus::MORALE) + rewardInfo.reward.extraComponents.emplace_back(Component::EComponentType::MORALE, 0, bonus.val, 0); + if (bonus.type == Bonus::LUCK) + rewardInfo.reward.extraComponents.emplace_back(Component::EComponentType::LUCK, 0, bonus.val, 0); + } + } +} + +void CTownRewardableBuilding::newTurn(CRandomGenerator & rand) const +{ + if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0) + { + if(configuration.resetParameters.rewards) + { + cb->setObjProperty(town->id, ObjProperty::REWARD_RANDOMIZE, indexOnTV); + } + if(configuration.resetParameters.visitors) + { + cb->setObjProperty(town->id, ObjProperty::REWARD_CLEARED, indexOnTV); + cb->setObjProperty(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, indexOnTV); + } + } } void CTownRewardableBuilding::setProperty(ui8 what, ui32 val) @@ -299,12 +335,18 @@ void CTownRewardableBuilding::setProperty(ui8 what, ui32 val) case ObjProperty::VISITORS: visitors.insert(ObjectInstanceID(val)); break; + case ObjProperty::STRUCTURE_CLEAR_VISITORS: + visitors.clear(); + break; case ObjProperty::REWARD_RANDOMIZE: - //initObj(cb->gameState()->getRandomGenerator()); + initObj(cb->gameState()->getRandomGenerator()); break; case ObjProperty::REWARD_SELECT: selectedReward = val; break; + case ObjProperty::REWARD_CLEARED: + onceVisitableObjectCleared = val; + break; } } @@ -355,7 +397,7 @@ bool CTownRewardableBuilding::wasVisitedBefore(const CGHeroInstance * contextHer case Rewardable::VISIT_UNLIMITED: return false; case Rewardable::VISIT_ONCE: - return false; //not supported + return onceVisitableObjectCleared; case Rewardable::VISIT_PLAYER: return false; //not supported case Rewardable::VISIT_BONUS: @@ -439,19 +481,12 @@ void CTownRewardableBuilding::onHeroVisit(const CGHeroInstance *h) const grantRewardWithMessage(rewards.front()); break; case Rewardable::SELECT_RANDOM: // give random - //TODO: support - //grantRewardWithMessage(*RandomGeneratorUtil::nextItem(rewards, cb->gameState()->getRandomGenerator())); + grantRewardWithMessage(*RandomGeneratorUtil::nextItem(rewards, cb->gameState()->getRandomGenerator())); break; } break; } } - - /*if(getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT).empty()) - { - ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, h->id); - cb->sendAndApply(&cov); - }*/ } else { diff --git a/lib/mapObjects/CGTownBuilding.h b/lib/mapObjects/CGTownBuilding.h index 2c6ebd0d7..94f3c81e0 100644 --- a/lib/mapObjects/CGTownBuilding.h +++ b/lib/mapObjects/CGTownBuilding.h @@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN class CGTownInstance; - +class CBuilding; class DLL_LINKAGE CGTownBuilding : public IObjectInterface { @@ -110,6 +110,8 @@ class DLL_LINKAGE CTownRewardableBuilding : public CGTownBuilding, public Reward /// reward selected by player, no serialize ui16 selectedReward = 0; + bool onceVisitableObjectCleared = false; + std::set visitors; bool wasVisitedBefore(const CGHeroInstance * contextHero) const; @@ -120,19 +122,25 @@ public: void setProperty(ui8 what, ui32 val) override; void onHeroVisit(const CGHeroInstance * h) const override; + void newTurn(CRandomGenerator & rand) const override; + /// gives second part of reward after hero level-ups for proper granting of spells/mana void heroLevelUpDone(const CGHeroInstance *hero) const override; + void initObj(CRandomGenerator & rand) override; + /// applies player selection of reward void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; - CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN); + CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * town, CRandomGenerator & rand); CTownRewardableBuilding() = default; template void serialize(Handler &h, const int version) { h & static_cast(*this); h & static_cast(*this); + h & onceVisitableObjectCleared; + h & visitors; } }; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 62078b211..464ac58c1 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -54,13 +54,13 @@ void CGTownInstance::setPropertyDer(ui8 what, ui32 val) switch (what) { case ObjProperty::STRUCTURE_ADD_VISITING_HERO: - bonusingBuildings[val]->setProperty (ObjProperty::VISITORS, visitingHero->id.getNum()); + bonusingBuildings[val]->setProperty(ObjProperty::VISITORS, visitingHero->id.getNum()); break; case ObjProperty::STRUCTURE_CLEAR_VISITORS: - bonusingBuildings[val]->setProperty (ObjProperty::STRUCTURE_CLEAR_VISITORS, 0); + bonusingBuildings[val]->setProperty(ObjProperty::STRUCTURE_CLEAR_VISITORS, 0); break; case ObjProperty::STRUCTURE_ADD_GARRISONED_HERO: //add garrisoned hero to visitors - bonusingBuildings[val]->setProperty (ObjProperty::VISITORS, garrisonHero->id.getNum()); + bonusingBuildings[val]->setProperty(ObjProperty::VISITORS, garrisonHero->id.getNum()); break; case ObjProperty::BONUS_VALUE_FIRST: bonusValue.first = val; @@ -68,6 +68,12 @@ void CGTownInstance::setPropertyDer(ui8 what, ui32 val) case ObjProperty::BONUS_VALUE_SECOND: bonusValue.second = val; break; + case ObjProperty::REWARD_RANDOMIZE: + bonusingBuildings[val]->setProperty(ObjProperty::REWARD_RANDOMIZE, 0); + break; + case ObjProperty::REWARD_CLEARED: + bonusingBuildings[val]->setProperty(ObjProperty::REWARD_CLEARED, false); + break; } } CGTownInstance::EFortLevel CGTownInstance::fortLevel() const //0 - none, 1 - fort, 2 - citadel, 3 - castle @@ -386,24 +392,7 @@ void CGTownInstance::addTownBonuses(CRandomGenerator & rand) bonusingBuildings.push_back(new COPWBonus(kvp.second->bid, kvp.second->subId, this)); if(kvp.second->subId == BuildingSubID::CONFIGURABLE_REWARD) - { - auto * newBuilding = new CTownRewardableBuilding(kvp.second->bid, kvp.second->subId, this); - kvp.second->rewardableObjectInfo.configureObject(newBuilding->configuration, rand); - for(auto & rewardInfo : newBuilding->configuration.info) - { - for (auto & bonus : rewardInfo.reward.bonuses) - { - bonus.source = Bonus::TOWN_STRUCTURE; - bonus.sid = kvp.second->bid; - //TODO: bonus.description = object->getObjectName(); - if (bonus.type == Bonus::MORALE) - rewardInfo.reward.extraComponents.emplace_back(Component::EComponentType::MORALE, 0, bonus.val, 0); - if (bonus.type == Bonus::LUCK) - rewardInfo.reward.extraComponents.emplace_back(Component::EComponentType::LUCK, 0, bonus.val, 0); - } - } - bonusingBuildings.push_back(newBuilding); - } + bonusingBuildings.push_back(new CTownRewardableBuilding(kvp.second->bid, kvp.second->subId, this, rand)); } } @@ -514,72 +503,73 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const cb->setObjProperty (id, ObjProperty::BONUS_VALUE_FIRST, resID); cb->setObjProperty (id, ObjProperty::BONUS_VALUE_SECOND, resVal); } - - const auto * manaVortex = getBonusingBuilding(BuildingSubID::MANA_VORTEX); - - if (manaVortex != nullptr) + + for(const auto * manaVortex : getBonusingBuildings(BuildingSubID::MANA_VORTEX)) cb->setObjProperty(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex - + //get Mana Vortex or Stables bonuses //same code is in the CGameHandler::buildStructure method if (visitingHero != nullptr) cb->visitCastleObjects(this, visitingHero); - + if (garrisonHero != nullptr) cb->visitCastleObjects(this, garrisonHero); - + if (tempOwner == PlayerColor::NEUTRAL) //garrison growth for neutral towns + { + std::vector nativeCrits; //slots + for(const auto & elem : Slots()) { - std::vector nativeCrits; //slots - for(const auto & elem : Slots()) + if (elem.second->type->getFaction() == subID) //native { - if (elem.second->type->getFaction() == subID) //native - { - nativeCrits.push_back(elem.first); //collect matching slots - } + nativeCrits.push_back(elem.first); //collect matching slots } - if(!nativeCrits.empty()) + } + if(!nativeCrits.empty()) + { + SlotID pos = *RandomGeneratorUtil::nextItem(nativeCrits, rand); + StackLocation sl(this, pos); + + const CCreature *c = getCreature(pos); + if (rand.nextInt(99) < 90 || c->upgrades.empty()) //increase number if no upgrade available { - SlotID pos = *RandomGeneratorUtil::nextItem(nativeCrits, rand); - StackLocation sl(this, pos); - - const CCreature *c = getCreature(pos); - if (rand.nextInt(99) < 90 || c->upgrades.empty()) //increase number if no upgrade available - { - cb->changeStackCount(sl, c->getGrowth()); - } - else //upgrade - { - cb->changeStackType(sl, VLC->creh->objects[*c->upgrades.begin()]); - } + cb->changeStackCount(sl, c->getGrowth()); } - if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack + else //upgrade { - int i = rand.nextInt(std::min(GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH) << 1) - 1); - if (!town->creatures[i].empty()) - { - CreatureID c = town->creatures[i][0]; - SlotID n; - - TQuantity count = creatureGrowth(i); - if (!count) // no dwelling - count = VLC->creh->objects[c]->getGrowth(); - - {//no lower tiers or above current month - - if ((n = getSlotFor(c)).validSlot()) - { - StackLocation sl(this, n); - if (slotEmpty(n)) - cb->insertNewStack(sl, VLC->creh->objects[c], count); - else //add to existing - cb->changeStackCount(sl, count); - } + cb->changeStackType(sl, VLC->creh->objects[*c->upgrades.begin()]); + } + } + if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack + { + int i = rand.nextInt(std::min(GameConstants::CREATURES_PER_TOWN, cb->getDate(Date::MONTH) << 1) - 1); + if (!town->creatures[i].empty()) + { + CreatureID c = town->creatures[i][0]; + SlotID n; + + TQuantity count = creatureGrowth(i); + if (!count) // no dwelling + count = VLC->creh->objects[c]->getGrowth(); + + {//no lower tiers or above current month + + if ((n = getSlotFor(c)).validSlot()) + { + StackLocation sl(this, n); + if (slotEmpty(n)) + cb->insertNewStack(sl, VLC->creh->objects[c], count); + else //add to existing + cb->changeStackCount(sl, count); } } } } + } } + + for(const auto * rewardableBuilding : getBonusingBuildings(BuildingSubID::CONFIGURABLE_REWARD)) + rewardableBuilding->newTurn(rand); } /* int3 CGTownInstance::getSightCenter() const @@ -832,7 +822,9 @@ void CGTownInstance::recreateBuildingsBonuses() void CGTownInstance::setVisitingHero(CGHeroInstance *h) { - assert(!!visitingHero == !h); + if(visitingHero.get() == h) + return; + if(h) { PlayerState *p = cb->gameState()->getPlayerState(h->tempOwner); @@ -855,7 +847,9 @@ void CGTownInstance::setVisitingHero(CGHeroInstance *h) void CGTownInstance::setGarrisonedHero(CGHeroInstance *h) { - assert(!!garrisonHero == !h); + if(garrisonHero.get() == h) + return; + if(h) { PlayerState *p = cb->gameState()->getPlayerState(h->tempOwner); @@ -933,14 +927,15 @@ const CArmedInstance * CGTownInstance::getUpperArmy() const return this; } -const CGTownBuilding * CGTownInstance::getBonusingBuilding(BuildingSubID::EBuildingSubID subId) const +std::vector CGTownInstance::getBonusingBuildings(BuildingSubID::EBuildingSubID subId) const { + std::vector ret; for(auto * const building : bonusingBuildings) { if(building->getBuildingSubtype() == subId) - return building; + ret.push_back(building); } - return nullptr; + return ret; } diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 167bf9f27..a68584b5f 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -150,7 +150,7 @@ public: GrowthInfo getGrowthInfo(int level) const; bool hasFort() const; bool hasCapitol() const; - const CGTownBuilding * getBonusingBuilding(BuildingSubID::EBuildingSubID subId) const; + std::vector getBonusingBuildings(BuildingSubID::EBuildingSubID subId) const; bool hasBuiltSomeTradeBuilding() const; //checks if special building with type buildingID is constructed bool hasBuilt(BuildingSubID::EBuildingSubID buildingID) const; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 6b2fda983..933a5b3d7 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4440,10 +4440,9 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl giveResource(player, EGameResID::GOLD, -GameConstants::HERO_GOLD_COST); - if (t) + if(t) { - visitCastleObjects(t, nh); - giveSpells (t,nh); + objectVisited(t, nh); } return true; }