/* * CBank.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 "CBank.h" #include #include #include "../texts/CGeneralTextHandler.h" #include "../IGameSettings.h" #include "../CPlayerState.h" #include "../mapObjectConstructors/CObjectClassesHandler.h" #include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../mapObjects/CGHeroInstance.h" #include "../networkPacks/Component.h" #include "../networkPacks/PacksForClient.h" #include "../networkPacks/PacksForClientBattle.h" #include "../IGameCallback.h" #include "../gameState/CGameState.h" VCMI_LIB_NAMESPACE_BEGIN ///helpers static std::string visitedTxt(const bool visited) { int id = visited ? 352 : 353; return VLC->generaltexth->allTexts[id]; } CBank::CBank(IGameCallback *cb) : CArmedInstance(cb) {} //must be instantiated in .cpp file for access to complete types of all member fields CBank::~CBank() = default; void CBank::initObj(vstd::RNG & rand) { daycounter = 0; resetDuration = 0; getObjectHandler()->configureObject(this, rand); } bool CBank::isCoastVisitable() const { return coastVisitable; } std::string CBank::getHoverText(PlayerColor player) const { if (!wasVisited(player)) return getObjectName(); return getObjectName() + "\n" + visitedTxt(bankConfig == nullptr); } std::vector CBank::getPopupComponents(PlayerColor player) const { if (!wasVisited(player)) return {}; if (!cb->getSettings().getBoolean(EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION)) return {}; if (bankConfig == nullptr) return {}; std::map guardsAmounts; std::vector result; for (auto const & slot : Slots()) if (slot.second) guardsAmounts[slot.second->getCreatureID()] += slot.second->getCount(); for (auto const & guard : guardsAmounts) { Component comp(ComponentType::CREATURE, guard.first, guard.second); result.push_back(comp); } return result; } void CBank::setConfig(const BankConfig & config) { bankConfig = std::make_unique(config); clearSlots(); // remove all stacks, if any for(const auto & stack : config.guards) setCreature (SlotID(stacksCount()), stack.getId(), stack.count); daycounter = 1; //yes, 1 since "today" daycounter won't be incremented } void CBank::setPropertyDer (ObjProperty what, ObjPropertyID identifier) { switch (what) { case ObjProperty::BANK_DAYCOUNTER: //daycounter daycounter+= identifier.getNum(); break; case ObjProperty::BANK_CLEAR: bankConfig.reset(); break; } } void CBank::newTurn(vstd::RNG & rand) const { if (bankConfig == nullptr) { if (resetDuration != 0) { if (daycounter >= resetDuration) { auto handler = std::dynamic_pointer_cast(getObjectHandler()); auto config = handler->generateConfiguration(cb, rand, ID); cb->setBankObjectConfiguration(id, config); } else cb->setObjPropertyValue(id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++ } } } bool CBank::wasVisited (PlayerColor player) const { return vstd::contains(cb->getPlayerState(player)->visitedObjects, ObjectInstanceID(id)); } void CBank::onHeroVisit(const CGHeroInstance * h) const { ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id); cb->sendAndApply(cov); BlockingDialog bd(true, false); bd.player = h->getOwner(); bd.text.appendLocalString(EMetaText::ADVOB_TXT, 32); bd.components = getPopupComponents(h->getOwner()); bd.text.replaceTextID(getObjectHandler()->getNameTextID()); cb->showBlockingDialog(this, &bd); } void CBank::doVisit(const CGHeroInstance * hero) const { InfoWindow iw; iw.type = EInfoWindowMode::AUTO; iw.player = hero->getOwner(); MetaString loot; if (!bankConfig) { iw.text.appendRawString(VLC->generaltexth->advobtxt[33]);// This was X, now is completely empty iw.text.replaceTextID(getObjectHandler()->getNameTextID()); cb->showInfoDialog(&iw); } //grant resources if (bankConfig) { for (GameResID it : GameResID::ALL_RESOURCES()) { if (bankConfig->resources[it] != 0) { iw.components.emplace_back(ComponentType::RESOURCE, it, bankConfig->resources[it]); loot.appendRawString("%d %s"); loot.replaceNumber(bankConfig->resources[it]); loot.replaceName(it); cb->giveResource(hero->getOwner(), it, bankConfig->resources[it]); } } //grant artifacts for (auto & elem : bankConfig->artifacts) { iw.components.emplace_back(ComponentType::ARTIFACT, elem); loot.appendRawString("%s"); loot.replaceName(elem); cb->giveHeroNewArtifact(hero, elem, ArtifactPosition::FIRST_AVAILABLE); } //display loot if (!iw.components.empty()) { iw.text.appendLocalString(EMetaText::ADVOB_TXT, 34); const auto * strongest = boost::range::max_element(bankConfig->guards, [](const CStackBasicDescriptor & a, const CStackBasicDescriptor & b) { return a.getType()->getFightValue() < b.getType()->getFightValue(); })->getType(); iw.text.replaceNamePlural(strongest->getId()); iw.text.replaceRawString(loot.buildList()); cb->showInfoDialog(&iw); } loot.clear(); iw.components.clear(); iw.text.clear(); if (!bankConfig->spells.empty()) { std::set spells; bool noWisdom = false; for(const SpellID & spellId : bankConfig->spells) { const auto * spell = spellId.toEntity(VLC); iw.text.appendName(spellId); if(spell->getLevel() <= hero->maxSpellLevel()) { if(hero->canLearnSpell(spell)) { spells.insert(spellId); iw.components.emplace_back(ComponentType::SPELL, spellId); } } else noWisdom = true; } if (!hero->getArt(ArtifactPosition::SPELLBOOK)) iw.text.appendLocalString(EMetaText::ADVOB_TXT, 109); //no spellbook else if(noWisdom) iw.text.appendLocalString(EMetaText::ADVOB_TXT, 108); //no expert Wisdom if(!iw.components.empty() || !iw.text.toString().empty()) cb->showInfoDialog(&iw); if(!spells.empty()) cb->changeSpells(hero, true, spells); } iw.components.clear(); iw.text.clear(); //grant creatures CCreatureSet ourArmy; for(const auto & slot : bankConfig->creatures) { ourArmy.addToSlot(ourArmy.getSlotFor(slot.getId()), slot.getId(), slot.count); } for(const auto & elem : ourArmy.Slots()) { iw.components.emplace_back(ComponentType::CREATURE, elem.second->getId(), elem.second->getCount()); loot.appendRawString("%s"); loot.replaceName(*elem.second); } if(ourArmy.stacksCount()) { if(ourArmy.stacksCount() == 1 && ourArmy.Slots().begin()->second->count == 1) iw.text.appendLocalString(EMetaText::ADVOB_TXT, 185); else iw.text.appendLocalString(EMetaText::ADVOB_TXT, 186); iw.text.replaceRawString(loot.buildList()); iw.text.replaceRawString(hero->getNameTranslated()); cb->showInfoDialog(&iw); cb->giveCreatures(this, hero, ourArmy, false); } cb->setObjPropertyValue(id, ObjProperty::BANK_CLEAR); //bc = nullptr } } void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { if (result.winner == BattleSide::ATTACKER) { doVisit(hero); } } void CBank::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const { if (answer) { if (bankConfig) // not looted bank cb->startBattle(hero, this); else doVisit(hero); } } VCMI_LIB_NAMESPACE_END