|
|
@@ -10,16 +10,19 @@
|
|
|
|
|
|
#include "StdInc.h"
|
|
|
#include "CRewardableObject.h"
|
|
|
-#include "../gameState/CGameState.h"
|
|
|
-#include "../texts/CGeneralTextHandler.h"
|
|
|
+
|
|
|
#include "../CPlayerState.h"
|
|
|
+#include "../GameSettings.h"
|
|
|
#include "../IGameCallback.h"
|
|
|
+#include "../gameState/CGameState.h"
|
|
|
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
|
|
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
|
|
#include "../mapObjectConstructors/CRewardableConstructor.h"
|
|
|
#include "../mapObjects/CGHeroInstance.h"
|
|
|
#include "../networkPacks/PacksForClient.h"
|
|
|
+#include "../networkPacks/PacksForClientBattle.h"
|
|
|
#include "../serializer/JsonSerializeFormat.h"
|
|
|
+#include "../texts/CGeneralTextHandler.h"
|
|
|
|
|
|
#include <vstd/RNG.h>
|
|
|
|
|
|
@@ -91,7 +94,42 @@ std::vector<Component> CRewardableObject::loadComponents(const CGHeroInstance *
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+bool CRewardableObject::guardedPotentially() const
|
|
|
+{
|
|
|
+ for (auto const & visitInfo : configuration.info)
|
|
|
+ if (!visitInfo.reward.guards.empty())
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool CRewardableObject::guardedPresently() const
|
|
|
+{
|
|
|
+ return stacksCount() > 0;
|
|
|
+}
|
|
|
+
|
|
|
void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
|
|
|
+{
|
|
|
+ if (guardedPresently())
|
|
|
+ {
|
|
|
+ auto guardedIndexes = getAvailableRewards(h, Rewardable::EEventType::EVENT_GUARDED);
|
|
|
+ auto guardedReward = configuration.info.at(guardedIndexes.at(0));
|
|
|
+
|
|
|
+ // ask player to confirm attack
|
|
|
+ BlockingDialog bd(true, false);
|
|
|
+ bd.player = h->getOwner();
|
|
|
+ bd.text = guardedReward.message;
|
|
|
+ bd.components = getPopupComponents(h->getOwner());
|
|
|
+
|
|
|
+ cb->showBlockingDialog(&bd);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ doHeroVisit(h);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CRewardableObject::doHeroVisit(const CGHeroInstance *h) const
|
|
|
{
|
|
|
if(!wasVisitedBefore(h))
|
|
|
{
|
|
|
@@ -181,39 +219,54 @@ void CRewardableObject::heroLevelUpDone(const CGHeroInstance *hero) const
|
|
|
grantRewardAfterLevelup(cb, configuration.info.at(selectedReward), this, hero);
|
|
|
}
|
|
|
|
|
|
-void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const
|
|
|
+void CRewardableObject::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
|
|
+{
|
|
|
+ if (result.winner == BattleSide::ATTACKER)
|
|
|
+ {
|
|
|
+ doHeroVisit(hero);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CRewardableObject::blockingDialogAnswered(const CGHeroInstance * hero, int32_t answer) const
|
|
|
{
|
|
|
- if(answer == 0)
|
|
|
+ if(guardedPresently())
|
|
|
+ {
|
|
|
+ if (answer)
|
|
|
+ cb->startBattleI(hero, this, true);
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- switch (configuration.visitMode)
|
|
|
+ if(answer == 0)
|
|
|
{
|
|
|
- case Rewardable::VISIT_UNLIMITED:
|
|
|
- case Rewardable::VISIT_BONUS:
|
|
|
- case Rewardable::VISIT_HERO:
|
|
|
- case Rewardable::VISIT_LIMITER:
|
|
|
+ switch(configuration.visitMode)
|
|
|
{
|
|
|
- // workaround for object with refusable reward not getting marked as visited
|
|
|
- // TODO: better solution that would also work for player-visitable objects
|
|
|
- if (!wasScouted(hero->getOwner()))
|
|
|
+ case Rewardable::VISIT_UNLIMITED:
|
|
|
+ case Rewardable::VISIT_BONUS:
|
|
|
+ case Rewardable::VISIT_HERO:
|
|
|
+ case Rewardable::VISIT_LIMITER:
|
|
|
{
|
|
|
- ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, hero->id);
|
|
|
- cb->sendAndApply(&cov);
|
|
|
+ // workaround for object with refusable reward not getting marked as visited
|
|
|
+ // TODO: better solution that would also work for player-visitable objects
|
|
|
+ if(!wasScouted(hero->getOwner()))
|
|
|
+ {
|
|
|
+ ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, hero->id);
|
|
|
+ cb->sendAndApply(&cov);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+ return; // player refused
|
|
|
}
|
|
|
|
|
|
- return; // player refused
|
|
|
- }
|
|
|
-
|
|
|
- if(answer > 0 && answer-1 < configuration.info.size())
|
|
|
- {
|
|
|
- auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
|
|
- markAsVisited(hero);
|
|
|
- grantReward(list[answer - 1], hero);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- throw std::runtime_error("Unhandled choice");
|
|
|
+ if(answer > 0 && answer - 1 < configuration.info.size())
|
|
|
+ {
|
|
|
+ auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
|
|
+ markAsVisited(hero);
|
|
|
+ grantReward(list[answer - 1], hero);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ throw std::runtime_error("Unhandled choice");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -368,19 +421,41 @@ std::vector<Component> CRewardableObject::getPopupComponentsImpl(PlayerColor pla
|
|
|
if (!configuration.showScoutedPreview)
|
|
|
return {};
|
|
|
|
|
|
- auto rewardIndices = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
|
|
- if (rewardIndices.empty() && !configuration.info.empty())
|
|
|
+ if (guardedPresently())
|
|
|
{
|
|
|
- // Object has valid config, but current hero has no rewards that he can receive.
|
|
|
- // Usually this happens if hero has already visited this object -> show reward using context without any hero
|
|
|
- // since reward may be context-sensitive - e.g. Witch Hut that gives 1 skill, but always at basic level
|
|
|
- return loadComponents(nullptr, {0});
|
|
|
+ if (!VLC->settings()->getBoolean(EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION))
|
|
|
+ return {};
|
|
|
+
|
|
|
+ std::map<CreatureID, int> guardsAmounts;
|
|
|
+ std::vector<Component> 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;
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ auto rewardIndices = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
|
|
+ if (rewardIndices.empty() && !configuration.info.empty())
|
|
|
+ {
|
|
|
+ // Object has valid config, but current hero has no rewards that he can receive.
|
|
|
+ // Usually this happens if hero has already visited this object -> show reward using context without any hero
|
|
|
+ // since reward may be context-sensitive - e.g. Witch Hut that gives 1 skill, but always at basic level
|
|
|
+ return loadComponents(nullptr, {0});
|
|
|
+ }
|
|
|
|
|
|
- if (rewardIndices.empty())
|
|
|
- return {};
|
|
|
+ if (rewardIndices.empty())
|
|
|
+ return {};
|
|
|
|
|
|
- return loadComponents(hero, rewardIndices);
|
|
|
+ return loadComponents(hero, rewardIndices);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
std::vector<Component> CRewardableObject::getPopupComponents(PlayerColor player) const
|
|
|
@@ -440,4 +515,21 @@ void CRewardableObject::serializeJsonOptions(JsonSerializeFormat & handler)
|
|
|
handler.serializeStruct("rewardable", static_cast<Rewardable::Interface&>(*this));
|
|
|
}
|
|
|
|
|
|
+void CRewardableObject::initializeGuards()
|
|
|
+{
|
|
|
+ clearSlots();
|
|
|
+
|
|
|
+ for (auto const & visitInfo : configuration.info)
|
|
|
+ {
|
|
|
+ for (auto const & guard : visitInfo.reward.guards)
|
|
|
+ {
|
|
|
+ auto slotID = getFreeSlot();
|
|
|
+ if (!slotID.validSlot())
|
|
|
+ return;
|
|
|
+
|
|
|
+ putStack(slotID, new CStackInstance(guard.getId(), guard.getCount()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
VCMI_LIB_NAMESPACE_END
|