diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index e3f683a09..25fbe9caa 100644 --- a/AI/Nullkiller/AIUtility.cpp +++ b/AI/Nullkiller/AIUtility.cpp @@ -319,7 +319,7 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj) //TODO: allow polling of remaining creatures in dwelling if(const auto * rewardable = dynamic_cast(obj)) - return rewardable->getResetDuration() == 7; + return rewardable->getConfiguration().getResetDuration() == 7; if(dynamic_cast(obj)) return true; diff --git a/AI/Nullkiller/Engine/AIMemory.cpp b/AI/Nullkiller/Engine/AIMemory.cpp index 1091f9664..4f75322f5 100644 --- a/AI/Nullkiller/Engine/AIMemory.cpp +++ b/AI/Nullkiller/Engine/AIMemory.cpp @@ -72,10 +72,10 @@ void AIMemory::markObjectVisited(const CGObjectInstance * obj) // TODO: maybe this logic belongs to CaptureObjects::shouldVisit if(const auto * rewardable = dynamic_cast(obj)) { - if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero + if (rewardable->getConfiguration().getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero return; - if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time + if (rewardable->getConfiguration().getVisitMode() == Rewardable::VISIT_BONUS) //or another time return; } diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 059967627..a4ce0f511 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1605,10 +1605,10 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj) if(const auto * rewardable = dynamic_cast(obj)) //we may want to visit it with another hero { - if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero + if (rewardable->getConfiguration().getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero return; - if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time + if (rewardable->getConfiguration().getVisitMode() == Rewardable::VISIT_BONUS) //or another time return; } @@ -2746,7 +2746,7 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj) { //TODO: allow polling of remaining creatures in dwelling if(const auto * rewardable = dynamic_cast(obj)) - return rewardable->getResetDuration() == 7; + return rewardable->getConfiguration().getResetDuration() == 7; if(dynamic_cast(obj)) return true; diff --git a/lib/mapObjects/CRewardableConstructor.cpp b/lib/mapObjects/CRewardableConstructor.cpp index 219d1f7ea..03c618221 100644 --- a/lib/mapObjects/CRewardableConstructor.cpp +++ b/lib/mapObjects/CRewardableConstructor.cpp @@ -47,7 +47,7 @@ void CRandomRewardObjectInfo::init(const JsonNode & objectConfig) parameters = objectConfig; } -TRewardLimitersList CRandomRewardObjectInfo::configureSublimiters(CRewardableObject * object, CRandomGenerator & rng, const JsonNode & source) const +TRewardLimitersList CRandomRewardObjectInfo::configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const { TRewardLimitersList result; for (const auto & input : source.Vector()) @@ -62,7 +62,7 @@ TRewardLimitersList CRandomRewardObjectInfo::configureSublimiters(CRewardableObj return result; } -void CRandomRewardObjectInfo::configureLimiter(CRewardableObject * object, CRandomGenerator & rng, CRewardLimiter & limiter, const JsonNode & source) const +void CRandomRewardObjectInfo::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardLimiter & limiter, const JsonNode & source) const { std::vector spells; for (size_t i=0; i<6; i++) @@ -91,7 +91,7 @@ void CRandomRewardObjectInfo::configureLimiter(CRewardableObject * object, CRand limiter.noneOf = configureSublimiters(object, rng, source["noneOf"] ); } -void CRandomRewardObjectInfo::configureReward(CRewardableObject * object, CRandomGenerator & rng, CRewardInfo & reward, const JsonNode & source) const +void CRandomRewardObjectInfo::configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardInfo & reward, const JsonNode & source) const { reward.resources = JsonRandom::loadResources(source["resources"], rng); @@ -111,17 +111,6 @@ void CRandomRewardObjectInfo::configureReward(CRewardableObject * object, CRando reward.removeObject = source["removeObject"].Bool(); reward.bonuses = JsonRandom::loadBonuses(source["bonuses"]); - for (auto & bonus : reward.bonuses) - { - bonus.source = Bonus::OBJECT; - bonus.sid = object->ID; - //TODO: bonus.description = object->getObjectName(); - if (bonus.type == Bonus::MORALE) - reward.extraComponents.emplace_back(Component::EComponentType::MORALE, 0, bonus.val, 0); - if (bonus.type == Bonus::LUCK) - reward.extraComponents.emplace_back(Component::EComponentType::LUCK, 0, bonus.val, 0); - } - reward.primary = JsonRandom::loadPrimary(source["primary"], rng); reward.secondary = JsonRandom::loadSecondary(source["secondary"], rng); @@ -149,7 +138,7 @@ void CRandomRewardObjectInfo::configureReward(CRewardableObject * object, CRando } } -void CRandomRewardObjectInfo::configureResetInfo(CRewardableObject * object, CRandomGenerator & rng, CRewardResetInfo & resetParameters, const JsonNode & source) const +void CRandomRewardObjectInfo::configureResetInfo(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardResetInfo & resetParameters, const JsonNode & source) const { resetParameters.period = static_cast(source["period"].Float()); resetParameters.visitors = source["visitors"].Bool(); @@ -157,7 +146,7 @@ void CRandomRewardObjectInfo::configureResetInfo(CRewardableObject * object, CRa } void CRandomRewardObjectInfo::configureRewards( - CRewardableObject * object, + Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source, std::map & thrownDice, @@ -200,13 +189,13 @@ void CRandomRewardObjectInfo::configureRewards( for (const auto & artifact : info.reward.spells ) info.message.addReplacement(MetaString::SPELL_NAME, artifact.getNum()); - object->info.push_back(info); + object.info.push_back(info); } } -void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRandomGenerator & rng) const +void CRandomRewardObjectInfo::configureObject(Rewardable::Configuration & object, CRandomGenerator & rng) const { - object->info.clear(); + object.info.clear(); std::map thrownDice; @@ -214,15 +203,14 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando configureRewards(object, rng, parameters["onVisited"], thrownDice, CRewardVisitInfo::EVENT_ALREADY_VISITED); configureRewards(object, rng, parameters["onEmpty"], thrownDice, CRewardVisitInfo::EVENT_NOT_AVAILABLE); - object->blockVisit= parameters["blockedVisitable"].Bool(); - object->onSelect = loadMessage(parameters["onSelectMessage"]); + object.onSelect = loadMessage(parameters["onSelectMessage"]); if (!parameters["onVisitedMessage"].isNull()) { CRewardVisitInfo onVisited; onVisited.visitType = CRewardVisitInfo::EVENT_ALREADY_VISITED; onVisited.message = loadMessage(parameters["onVisitedMessage"]); - object->info.push_back(onVisited); + object.info.push_back(onVisited); } if (!parameters["onEmptyMessage"].isNull()) @@ -230,24 +218,24 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando CRewardVisitInfo onEmpty; onEmpty.visitType = CRewardVisitInfo::EVENT_NOT_AVAILABLE; onEmpty.message = loadMessage(parameters["onEmptyMessage"]); - object->info.push_back(onEmpty); + object.info.push_back(onEmpty); } - configureResetInfo(object, rng, object->resetParameters, parameters["resetParameters"]); + configureResetInfo(object, rng, object.resetParameters, parameters["resetParameters"]); - object->canRefuse = parameters["canRefuse"].Bool(); + object.canRefuse = parameters["canRefuse"].Bool(); if(parameters["showInInfobox"].isNull()) - object->infoWindowType = EInfoWindowMode::AUTO; + object.infoWindowType = EInfoWindowMode::AUTO; else - object->infoWindowType = parameters["showInInfobox"].Bool() ? EInfoWindowMode::INFO : EInfoWindowMode::MODAL; + object.infoWindowType = parameters["showInInfobox"].Bool() ? EInfoWindowMode::INFO : EInfoWindowMode::MODAL; auto visitMode = parameters["visitMode"].String(); for(int i = 0; i < Rewardable::VisitModeString.size(); ++i) { if(Rewardable::VisitModeString[i] == visitMode) { - object->visitMode = i; + object.visitMode = i; break; } } @@ -257,7 +245,7 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando { if(Rewardable::SelectModeString[i] == selectMode) { - object->selectMode = i; + object.selectMode = i; break; } } @@ -321,9 +309,11 @@ const JsonNode & CRandomRewardObjectInfo::getParameters() const void CRewardableConstructor::initTypeData(const JsonNode & config) { objectInfo.init(config); + blockVisit = config["blockedVisitable"].Bool(); if (!config["name"].isNull()) VLC->generaltexth->registerString( config.meta, getNameTextID(), config["name"].String()); + } bool CRewardableConstructor::hasNameTextID() const @@ -336,12 +326,29 @@ CGObjectInstance * CRewardableConstructor::create(std::shared_ptrappearance = tmpl; + ret->blockVisit = blockVisit; return ret; } void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const { - objectInfo.configureObject(dynamic_cast(object), rng); + if(auto * rewardableObject = dynamic_cast(object)) + { + objectInfo.configureObject(rewardableObject->configuration, rng); + for(auto & rewardInfo : rewardableObject->configuration.info) + { + for (auto & bonus : rewardInfo.reward.bonuses) + { + bonus.source = Bonus::OBJECT; + bonus.sid = rewardableObject->ID; + //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); + } + } + } } std::unique_ptr CRewardableConstructor::getObjectInfo(std::shared_ptr tmpl) const diff --git a/lib/mapObjects/CRewardableConstructor.h b/lib/mapObjects/CRewardableConstructor.h index f47571410..0d52dcd1a 100644 --- a/lib/mapObjects/CRewardableConstructor.h +++ b/lib/mapObjects/CRewardableConstructor.h @@ -20,13 +20,13 @@ class DLL_LINKAGE CRandomRewardObjectInfo : public IObjectInfo { JsonNode parameters; - void configureRewards(CRewardableObject * object, CRandomGenerator & rng, const JsonNode & source, std::map & thrownDice, CRewardVisitInfo::ERewardEventType mode) const; + void configureRewards(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source, std::map & thrownDice, CRewardVisitInfo::ERewardEventType mode) const; - void configureLimiter(CRewardableObject * object, CRandomGenerator & rng, CRewardLimiter & limiter, const JsonNode & source) const; - TRewardLimitersList configureSublimiters(CRewardableObject * object, CRandomGenerator & rng, const JsonNode & source) const; + void configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardLimiter & limiter, const JsonNode & source) const; + TRewardLimitersList configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const; - void configureReward(CRewardableObject * object, CRandomGenerator & rng, CRewardInfo & info, const JsonNode & source) const; - void configureResetInfo(CRewardableObject * object, CRandomGenerator & rng, CRewardResetInfo & info, const JsonNode & source) const; + void configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardInfo & info, const JsonNode & source) const; + void configureResetInfo(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardResetInfo & info, const JsonNode & source) const; public: const JsonNode & getParameters() const; @@ -45,7 +45,7 @@ public: bool givesBonuses() const override; - void configureObject(CRewardableObject * object, CRandomGenerator & rng) const; + void configureObject(Rewardable::Configuration & object, CRandomGenerator & rng) const; void init(const JsonNode & objectConfig); @@ -60,6 +60,8 @@ class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler CRandomRewardObjectInfo objectInfo; void initTypeData(const JsonNode & config) override; + + bool blockVisit = false; public: bool hasNameTextID() const override; diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index c15c1f3e3..978fe38fb 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -114,7 +114,78 @@ bool CRewardLimiter::heroAllowed(const CGHeroInstance * hero) const return false; } -std::vector CRewardableObject::getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event) const +si32 CRewardInfo::calculateManaPoints(const CGHeroInstance * hero) const +{ + si32 manaScaled = hero->mana; + if (manaPercentage >= 0) + manaScaled = hero->manaLimit() * manaPercentage / 100; + + si32 manaMissing = std::max(0, hero->manaLimit() - manaScaled); + si32 manaGranted = std::min(manaMissing, manaDiff); + si32 manaOverflow = manaDiff - manaGranted; + si32 manaOverLimit = manaOverflow * manaOverflowFactor / 100; + si32 manaOutput = manaScaled + manaGranted + manaOverLimit; + + return manaOutput; +} + +Component CRewardInfo::getDisplayedComponent(const CGHeroInstance * h) const +{ + std::vector comps; + loadComponents(comps, h); + assert(!comps.empty()); + return comps.front(); +} + +void CRewardInfo::loadComponents(std::vector & comps, + const CGHeroInstance * h) const +{ + for (auto comp : extraComponents) + comps.push_back(comp); + + if (heroExperience) + { + comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(h->calculateXp(heroExperience)), 0); + } + if (heroLevel) + comps.emplace_back(Component::EComponentType::EXPERIENCE, 1, heroLevel, 0); + + if (manaDiff || manaPercentage >= 0) + comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, calculateManaPoints(h) - h->mana, 0); + + for (size_t i=0; i(i), primary[i], 0); + } + + for(const auto & entry : secondary) + comps.emplace_back(Component::EComponentType::SEC_SKILL, entry.first, entry.second, 0); + + for(const auto & entry : artifacts) + comps.emplace_back(Component::EComponentType::ARTIFACT, entry, 1, 0); + + for(const auto & entry : spells) + comps.emplace_back(Component::EComponentType::SPELL, entry, 1, 0); + + for(const auto & entry : creatures) + comps.emplace_back(Component::EComponentType::CREATURE, entry.type->getId(), entry.count, 0); + + for (size_t i=0; i(i), resources[i], 0); + } +} + +// FIXME: copy-pasted from CObjectHandler +static std::string visitedTxt(const bool visited) +{ + int id = visited ? 352 : 353; + return VLC->generaltexth->allTexts[id]; +} + +std::vector Rewardable::Configuration::getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event) const { std::vector ret; @@ -131,20 +202,35 @@ std::vector CRewardableObject::getAvailableRewards(const CGHeroInstance * return ret; } +Rewardable::EVisitMode Rewardable::Configuration::getVisitMode() const +{ + return static_cast(visitMode); +} + +ui16 Rewardable::Configuration::getResetDuration() const +{ + return resetParameters.period; +} + + + + + + void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const { auto grantRewardWithMessage = [&](int index, bool markAsVisit) -> void { - auto vi = info[index]; + auto vi = configuration.info[index]; logGlobal->debug("Granting reward %d. Message says: %s", index, vi.message.toString()); // show message only if it is not empty or in infobox - if (infoWindowType != EInfoWindowMode::MODAL || !vi.message.toString().empty()) + if (configuration.infoWindowType != EInfoWindowMode::MODAL || !vi.message.toString().empty()) { InfoWindow iw; iw.player = h->tempOwner; iw.text = vi.message; vi.reward.loadComponents(iw.components, h); - iw.type = infoWindowType; + iw.type = configuration.infoWindowType; if(!iw.components.empty() || !iw.text.toString().empty()) cb->showInfoDialog(&iw); } @@ -153,27 +239,27 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const }; auto selectRewardsMessage = [&](const std::vector & rewards, const MetaString & dialog) -> void { - BlockingDialog sd(canRefuse, rewards.size() > 1); + BlockingDialog sd(configuration.canRefuse, rewards.size() > 1); sd.player = h->tempOwner; sd.text = dialog; if (rewards.size() > 1) for (auto index : rewards) - sd.components.push_back(info[index].reward.getDisplayedComponent(h)); + sd.components.push_back(configuration.info[index].reward.getDisplayedComponent(h)); if (rewards.size() == 1) - info[rewards[0]].reward.loadComponents(sd.components, h); + configuration.info[rewards[0]].reward.loadComponents(sd.components, h); cb->showBlockingDialog(&sd); }; if(!wasVisitedBefore(h)) { - auto rewards = getAvailableRewards(h, CRewardVisitInfo::EVENT_FIRST_VISIT); + auto rewards = configuration.getAvailableRewards(h, CRewardVisitInfo::EVENT_FIRST_VISIT); bool objectRemovalPossible = false; for(auto index : rewards) { - if(info[index].reward.removeObject) + if(configuration.info[index].reward.removeObject) objectRemovalPossible = true; } @@ -182,7 +268,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const { case 0: // no available rewards, e.g. visiting School of War without gold { - auto emptyRewards = getAvailableRewards(h, CRewardVisitInfo::EVENT_NOT_AVAILABLE); + auto emptyRewards = configuration.getAvailableRewards(h, CRewardVisitInfo::EVENT_NOT_AVAILABLE); if (!emptyRewards.empty()) grantRewardWithMessage(emptyRewards[0], false); else @@ -191,19 +277,19 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const } case 1: // one reward. Just give it with message { - if (canRefuse) - selectRewardsMessage(rewards, info[rewards[0]].message); + if (configuration.canRefuse) + selectRewardsMessage(rewards, configuration.info[rewards[0]].message); else grantRewardWithMessage(rewards[0], true); break; } default: // multiple rewards. Act according to select mode { - switch (selectMode) { - case SELECT_PLAYER: // player must select - selectRewardsMessage(rewards, onSelect); + switch (configuration.selectMode) { + case Rewardable::SELECT_PLAYER: // player must select + selectRewardsMessage(rewards, configuration.onSelect); break; - case SELECT_FIRST: // give first available + case Rewardable::SELECT_FIRST: // give first available grantRewardWithMessage(rewards[0], true); break; } @@ -211,7 +297,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const } } - if(!objectRemovalPossible && getAvailableRewards(h, CRewardVisitInfo::EVENT_FIRST_VISIT).empty()) + if(!objectRemovalPossible && configuration.getAvailableRewards(h, CRewardVisitInfo::EVENT_FIRST_VISIT).empty()) { ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, h->id); cb->sendAndApply(&cov); @@ -221,7 +307,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const { logGlobal->debug("Revisiting already visited object"); - auto visitedRewards = getAvailableRewards(h, CRewardVisitInfo::EVENT_ALREADY_VISITED); + auto visitedRewards = configuration.getAvailableRewards(h, CRewardVisitInfo::EVENT_ALREADY_VISITED); if (!visitedRewards.empty()) grantRewardWithMessage(visitedRewards[0], false); else @@ -231,7 +317,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const void CRewardableObject::heroLevelUpDone(const CGHeroInstance *hero) const { - grantRewardAfterLevelup(info[selectedReward], hero); + grantRewardAfterLevelup(configuration.info[configuration.selectedReward], hero); } void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const @@ -239,9 +325,9 @@ void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32 if(answer == 0) return; // player refused - if(answer > 0 && answer-1 < info.size()) + if(answer > 0 && answer-1 < configuration.info.size()) { - auto list = getAvailableRewards(hero, CRewardVisitInfo::EVENT_FIRST_VISIT); + auto list = configuration.getAvailableRewards(hero, CRewardVisitInfo::EVENT_FIRST_VISIT); grantReward(list[answer - 1], hero, true); } else @@ -261,7 +347,7 @@ void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero, } cb->setObjProperty(id, ObjProperty::REWARD_SELECT, rewardID); - grantRewardBeforeLevelup(info[rewardID], hero); + grantRewardBeforeLevelup(configuration.info[rewardID], hero); } void CRewardableObject::grantRewardBeforeLevelup(const CRewardVisitInfo & info, const CGHeroInstance * hero) const @@ -385,17 +471,17 @@ void CRewardableObject::grantRewardAfterLevelup(const CRewardVisitInfo & info, c bool CRewardableObject::wasVisitedBefore(const CGHeroInstance * contextHero) const { - switch (visitMode) + switch (configuration.visitMode) { - case VISIT_UNLIMITED: + case Rewardable::VISIT_UNLIMITED: return false; - case VISIT_ONCE: - return onceVisitableObjectCleared; - case VISIT_PLAYER: + case Rewardable::VISIT_ONCE: + return configuration.onceVisitableObjectCleared; + case Rewardable::VISIT_PLAYER: return vstd::contains(cb->getPlayerState(contextHero->getOwner())->visitedObjects, ObjectInstanceID(id)); - case VISIT_BONUS: + case Rewardable::VISIT_BONUS: return contextHero->hasBonusFrom(Bonus::OBJECT, ID); - case VISIT_HERO: + case Rewardable::VISIT_HERO: return contextHero->visitedObjects.count(ObjectInstanceID(id)); default: return false; @@ -405,14 +491,14 @@ bool CRewardableObject::wasVisitedBefore(const CGHeroInstance * contextHero) con bool CRewardableObject::wasVisited(PlayerColor player) const { - switch (visitMode) + switch (configuration.visitMode) { - case VISIT_UNLIMITED: - case VISIT_BONUS: - case VISIT_HERO: + case Rewardable::VISIT_UNLIMITED: + case Rewardable::VISIT_BONUS: + case Rewardable::VISIT_HERO: return false; - case VISIT_ONCE: - case VISIT_PLAYER: + case Rewardable::VISIT_ONCE: + case Rewardable::VISIT_PLAYER: return vstd::contains(cb->getPlayerState(player)->visitedObjects, ObjectInstanceID(id)); default: return false; @@ -421,108 +507,27 @@ bool CRewardableObject::wasVisited(PlayerColor player) const bool CRewardableObject::wasVisited(const CGHeroInstance * h) const { - switch (visitMode) + switch (configuration.visitMode) { - case VISIT_BONUS: + case Rewardable::VISIT_BONUS: return h->hasBonusFrom(Bonus::OBJECT, ID); - case VISIT_HERO: + case Rewardable::VISIT_HERO: return h->visitedObjects.count(ObjectInstanceID(id)); default: return wasVisited(h->tempOwner); } } -CRewardableObject::EVisitMode CRewardableObject::getVisitMode() const -{ - return static_cast(visitMode); -} - -ui16 CRewardableObject::getResetDuration() const -{ - return resetParameters.period; -} - -void CRewardInfo::loadComponents(std::vector & comps, - const CGHeroInstance * h) const -{ - for (auto comp : extraComponents) - comps.push_back(comp); - - if (heroExperience) - { - comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(h->calculateXp(heroExperience)), 0); - } - if (heroLevel) - comps.emplace_back(Component::EComponentType::EXPERIENCE, 1, heroLevel, 0); - - if (manaDiff || manaPercentage >= 0) - comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, calculateManaPoints(h) - h->mana, 0); - - for (size_t i=0; i(i), primary[i], 0); - } - - for(const auto & entry : secondary) - comps.emplace_back(Component::EComponentType::SEC_SKILL, entry.first, entry.second, 0); - - for(const auto & entry : artifacts) - comps.emplace_back(Component::EComponentType::ARTIFACT, entry, 1, 0); - - for(const auto & entry : spells) - comps.emplace_back(Component::EComponentType::SPELL, entry, 1, 0); - - for(const auto & entry : creatures) - comps.emplace_back(Component::EComponentType::CREATURE, entry.type->getId(), entry.count, 0); - - for (size_t i=0; i(i), resources[i], 0); - } -} - -si32 CRewardInfo::calculateManaPoints(const CGHeroInstance * hero) const -{ - si32 manaScaled = hero->mana; - if (manaPercentage >= 0) - manaScaled = hero->manaLimit() * manaPercentage / 100; - - si32 manaMissing = std::max(0, hero->manaLimit() - manaScaled); - si32 manaGranted = std::min(manaMissing, manaDiff); - si32 manaOverflow = manaDiff - manaGranted; - si32 manaOverLimit = manaOverflow * manaOverflowFactor / 100; - si32 manaOutput = manaScaled + manaGranted + manaOverLimit; - - return manaOutput; -} - -Component CRewardInfo::getDisplayedComponent(const CGHeroInstance * h) const -{ - std::vector comps; - loadComponents(comps, h); - assert(!comps.empty()); - return comps.front(); -} - -// FIXME: copy-pasted from CObjectHandler -static std::string visitedTxt(const bool visited) -{ - int id = visited ? 352 : 353; - return VLC->generaltexth->allTexts[id]; -} - std::string CRewardableObject::getHoverText(PlayerColor player) const { - if(visitMode == VISIT_PLAYER || visitMode == VISIT_ONCE) + if(configuration.visitMode == Rewardable::VISIT_PLAYER || configuration.visitMode == Rewardable::VISIT_ONCE) return getObjectName() + " " + visitedTxt(wasVisited(player)); return getObjectName(); } std::string CRewardableObject::getHoverText(const CGHeroInstance * hero) const { - if(visitMode != VISIT_UNLIMITED) + if(configuration.visitMode != Rewardable::VISIT_UNLIMITED) return getObjectName() + " " + visitedTxt(wasVisited(hero)); return getObjectName(); } @@ -535,47 +540,43 @@ void CRewardableObject::setPropertyDer(ui8 what, ui32 val) initObj(cb->gameState()->getRandomGenerator()); break; case ObjProperty::REWARD_SELECT: - selectedReward = val; + configuration.selectedReward = val; break; case ObjProperty::REWARD_CLEARED: - onceVisitableObjectCleared = val; + configuration.onceVisitableObjectCleared = val; break; } } -void CRewardableObject::triggerReset() const -{ - if (resetParameters.rewards) - { - cb->setObjProperty(id, ObjProperty::REWARD_RANDOMIZE, 0); - } - if (resetParameters.visitors) - { - cb->setObjProperty(id, ObjProperty::REWARD_CLEARED, false); - - ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_CLEAR, id); - cb->sendAndApply(&cov); - } -} - void CRewardableObject::newTurn(CRandomGenerator & rand) const { - if (resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % resetParameters.period) == 0) - triggerReset(); + 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(id, ObjProperty::REWARD_RANDOMIZE, 0); + } + if (configuration.resetParameters.visitors) + { + cb->setObjProperty(id, ObjProperty::REWARD_CLEARED, false); + ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_CLEAR, id); + cb->sendAndApply(&cov); + } + } } void CRewardableObject::initObj(CRandomGenerator & rand) { VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); - assert(!info.empty()); + assert(!configuration.info.empty()); } -CRewardableObject::CRewardableObject(): - selectMode(0), - visitMode(0), - selectedReward(0), - onceVisitableObjectCleared(false), - canRefuse(false) +CRewardableObject::CRewardableObject() {} +const Rewardable::Configuration CRewardableObject::getConfiguration() const +{ + return configuration; +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 74b7a413e..5144a43a1 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -183,6 +183,7 @@ public: /// Generates list of components that describes reward for a specific hero virtual void loadComponents(std::vector & comps, const CGHeroInstance * h) const; + Component getDisplayedComponent(const CGHeroInstance * h) const; si32 calculateManaPoints(const CGHeroInstance * h) const; @@ -256,21 +257,6 @@ public: namespace Rewardable { - const std::array SelectModeString{"selectFirst", "selectPlayer"}; - const std::array VisitModeString{"unlimited", "once", "hero", "bonus", "player"}; -} - -/// Base class that can handle granting rewards to visiting heroes. -/// Inherits from CArmedInstance for proper trasfer of armies -class DLL_LINKAGE CRewardableObject : public CArmedInstance -{ - /// function that must be called if hero got level-up during grantReward call - void grantRewardAfterLevelup(const CRewardVisitInfo & reward, const CGHeroInstance * hero) const; - - /// grants reward to hero - void grantRewardBeforeLevelup(const CRewardVisitInfo & reward, const CGHeroInstance * hero) const; - -public: enum EVisitMode { VISIT_UNLIMITED, // any number of times. Side effect - object hover text won't contain visited/not visited text @@ -280,7 +266,6 @@ public: VISIT_PLAYER // every player can visit object once }; -protected: /// controls selection of reward granted to player enum ESelectMode { @@ -288,58 +273,92 @@ protected: SELECT_PLAYER, // player can select from all allowed rewards }; - /// filters list of visit info and returns rewards that can be granted to current hero - virtual std::vector getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event ) const; + const std::array SelectModeString{"selectFirst", "selectPlayer"}; + const std::array VisitModeString{"unlimited", "once", "hero", "bonus", "player"}; - virtual void grantReward(ui32 rewardID, const CGHeroInstance * hero, bool markVisited) const; + /// Base class that can handle granting rewards to visiting heroes. + class DLL_LINKAGE Configuration + { + public: + /// Message that will be shown if player needs to select one of multiple rewards + MetaString onSelect; - virtual void triggerReset() const; + /// Rewards that can be applied by an object + std::vector info; - /// Message that will be shown if player needs to select one of multiple rewards - MetaString onSelect; + /// how reward will be selected, uses ESelectMode enum + ui8 selectMode = Rewardable::SELECT_FIRST; - /// Rewards that can be applied by an object - std::vector info; + /// contols who can visit an object, uses EVisitMode enum + ui8 visitMode = Rewardable::VISIT_UNLIMITED; - /// how reward will be selected, uses ESelectMode enum - ui8 selectMode; + /// reward selected by player + ui16 selectedReward = 0; - /// contols who can visit an object, uses EVisitMode enum - ui8 visitMode; + /// how and when should the object be reset + CRewardResetInfo resetParameters; - /// reward selected by player - ui16 selectedReward; + /// if true - player can refuse visiting an object (e.g. Tomb) + bool canRefuse = false; - /// how and when should the object be reset - CRewardResetInfo resetParameters; + /// if true - object info will shown in infobox (like resource pickup) + EInfoWindowMode infoWindowType = EInfoWindowMode::AUTO; + + bool onceVisitableObjectCleared = false; + + EVisitMode getVisitMode() const; + ui16 getResetDuration() const; + + /// filters list of visit info and returns rewards that can be granted to current hero + virtual std::vector getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event ) const; + + void initObj(CRandomGenerator & rand); + + template void serialize(Handler &h, const int version) + { + h & info; + h & canRefuse; + h & resetParameters; + h & onSelect; + h & visitMode; + h & selectMode; + h & selectedReward; + h & onceVisitableObjectCleared; + h & infoWindowType; + } + }; +} - /// if true - player can refuse visiting an object (e.g. Tomb) - bool canRefuse; +/// Base class that can handle granting rewards to visiting heroes. +/// Inherits from CArmedInstance for proper trasfer of armies +class DLL_LINKAGE CRewardableObject : public CArmedInstance +{ +protected: + Rewardable::Configuration configuration; + + /// function that must be called if hero got level-up during grantReward call + void grantRewardAfterLevelup(const CRewardVisitInfo & reward, const CGHeroInstance * hero) const; - /// if true - object info will shown in infobox (like resource pickup) - EInfoWindowMode infoWindowType = EInfoWindowMode::AUTO; - - /// return true if this object was "cleared" before and no longer has rewards applicable to selected hero - /// unlike wasVisited, this method uses information not available to player owner, for example, if object was cleared by another player before - bool wasVisitedBefore(const CGHeroInstance * contextHero) const; - - bool onceVisitableObjectCleared; + /// grants reward to hero + void grantRewardBeforeLevelup(const CRewardVisitInfo & reward, const CGHeroInstance * hero) const; /// caster to cast adveture spells mutable spells::ExternalCaster caster; public: - EVisitMode getVisitMode() const; - ui16 getResetDuration() const; - - void setPropertyDer(ui8 what, ui32 val) override; - std::string getHoverText(PlayerColor player) const override; - std::string getHoverText(const CGHeroInstance * hero) const override; - + + const Rewardable::Configuration getConfiguration() const; + + /// return true if this object was "cleared" before and no longer has rewards applicable to selected hero + /// unlike wasVisited, this method uses information not available to player owner, for example, if object was cleared by another player before + bool wasVisitedBefore(const CGHeroInstance * contextHero) const; + + virtual void grantReward(ui32 rewardID, const CGHeroInstance * hero, bool markVisited) const; + /// Visitability checks. Note that hero check includes check for hero owner (returns true if object was visited by player) bool wasVisited(PlayerColor player) const override; bool wasVisited(const CGHeroInstance * h) const override; - + /// gives reward to player or ask for choice in case of multiple rewards void onHeroVisit(const CGHeroInstance *h) const override; @@ -355,24 +374,17 @@ public: void initObj(CRandomGenerator & rand) override; CRewardableObject(); + + void setPropertyDer(ui8 what, ui32 val) override; + std::string getHoverText(PlayerColor player) const override; + std::string getHoverText(const CGHeroInstance * hero) const override; template void serialize(Handler &h, const int version) { h & static_cast(*this); - h & info; - h & canRefuse; - h & resetParameters; - h & onSelect; - h & visitMode; - h & selectMode; - h & selectedReward; - h & onceVisitableObjectCleared; - if (version >= 817) - h & infoWindowType; } - - // for configuration/object setup - friend class CRandomRewardObjectInfo; + + friend class CRewardableConstructor; }; //TODO: