1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

Rewardable objects may now define guards. Converted Crypt to rewardable.

This commit is contained in:
Ivan Savenko
2024-08-30 14:21:44 +00:00
parent a9c4683da6
commit 785036836c
12 changed files with 209 additions and 105 deletions

View File

@@ -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(answer == 0)
if (result.winner == BattleSide::ATTACKER)
{
switch (configuration.visitMode)
{
case Rewardable::VISIT_UNLIMITED:
case Rewardable::VISIT_BONUS:
case Rewardable::VISIT_HERO:
case Rewardable::VISIT_LIMITER:
{
// 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
doHeroVisit(hero);
}
}
if(answer > 0 && answer-1 < configuration.info.size())
void CRewardableObject::blockingDialogAnswered(const CGHeroInstance * hero, int32_t answer) const
{
if(guardedPresently())
{
auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
markAsVisited(hero);
grantReward(list[answer - 1], hero);
if (answer)
cb->startBattleI(hero, this, true);
}
else
{
throw std::runtime_error("Unhandled choice");
if(answer == 0)
{
switch(configuration.visitMode)
{
case Rewardable::VISIT_UNLIMITED:
case Rewardable::VISIT_BONUS:
case Rewardable::VISIT_HERO:
case Rewardable::VISIT_LIMITER:
{
// 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
}
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