diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index 25fbe9caa..32b4c06af 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->getConfiguration().getResetDuration() == 7; + return rewardable->configuration.getResetDuration() == 7; if(dynamic_cast(obj)) return true; diff --git a/AI/Nullkiller/Engine/AIMemory.cpp b/AI/Nullkiller/Engine/AIMemory.cpp index 4f75322f5..c8e8a8a56 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->getConfiguration().getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero + if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero return; - if (rewardable->getConfiguration().getVisitMode() == Rewardable::VISIT_BONUS) //or another time + if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_BONUS) //or another time return; } diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index a4ce0f511..0ff79cce2 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->getConfiguration().getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero + if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero return; - if (rewardable->getConfiguration().getVisitMode() == Rewardable::VISIT_BONUS) //or another time + if (rewardable->configuration.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->getConfiguration().getResetDuration() == 7; + return rewardable->configuration.getResetDuration() == 7; if(dynamic_cast(obj)) return true; diff --git a/IDETemplateMacros.plist b/IDETemplateMacros.plist new file mode 100644 index 000000000..830a28b80 --- /dev/null +++ b/IDETemplateMacros.plist @@ -0,0 +1,12 @@ + + + + + + diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 8fc80a4fc..436dc2a0a 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -65,13 +65,10 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjects/CObjectHandler.cpp ${MAIN_LIB_DIR}/mapObjects/CommonConstructors.cpp ${MAIN_LIB_DIR}/mapObjects/CQuest.cpp - ${MAIN_LIB_DIR}/mapObjects/CRandomRewardObjectInfo.cpp ${MAIN_LIB_DIR}/mapObjects/CRewardableConstructor.cpp ${MAIN_LIB_DIR}/mapObjects/CRewardableObject.cpp - ${MAIN_LIB_DIR}/mapObjects/JsonRandom.cpp ${MAIN_LIB_DIR}/mapObjects/MiscObjects.cpp ${MAIN_LIB_DIR}/mapObjects/ObjectTemplate.cpp - ${MAIN_LIB_DIR}/mapObjects/Rewardable.cpp ${MAIN_LIB_DIR}/mapping/CCampaignHandler.cpp ${MAIN_LIB_DIR}/mapping/CDrawRoadsOperation.cpp @@ -95,6 +92,12 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/registerTypes/TypesLobbyPacks.cpp ${MAIN_LIB_DIR}/registerTypes/TypesServerPacks.cpp + ${MAIN_LIB_DIR}/rewardable/Configuration.cpp + ${MAIN_LIB_DIR}/rewardable/Info.cpp + ${MAIN_LIB_DIR}/rewardable/Interface.cpp + ${MAIN_LIB_DIR}/rewardable/Limiter.cpp + ${MAIN_LIB_DIR}/rewardable/Reward.cpp + ${MAIN_LIB_DIR}/rmg/RmgArea.cpp ${MAIN_LIB_DIR}/rmg/RmgObject.cpp ${MAIN_LIB_DIR}/rmg/RmgPath.cpp @@ -200,6 +203,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/IHandlerBase.cpp ${MAIN_LIB_DIR}/JsonDetail.cpp ${MAIN_LIB_DIR}/JsonNode.cpp + ${MAIN_LIB_DIR}/JsonRandom.cpp ${MAIN_LIB_DIR}/LoadProgress.cpp ${MAIN_LIB_DIR}/LogicalExpression.cpp ${MAIN_LIB_DIR}/NetPacksLib.cpp @@ -339,14 +343,11 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjects/CObjectHandler.h ${MAIN_LIB_DIR}/mapObjects/CommonConstructors.h ${MAIN_LIB_DIR}/mapObjects/CQuest.h - ${MAIN_LIB_DIR}/mapObjects/CRandomRewardObjectInfo.h ${MAIN_LIB_DIR}/mapObjects/CRewardableConstructor.h ${MAIN_LIB_DIR}/mapObjects/CRewardableObject.h - ${MAIN_LIB_DIR}/mapObjects/JsonRandom.h ${MAIN_LIB_DIR}/mapObjects/MapObjects.h ${MAIN_LIB_DIR}/mapObjects/MiscObjects.h ${MAIN_LIB_DIR}/mapObjects/ObjectTemplate.h - ${MAIN_LIB_DIR}/mapObjects/Rewardable.h ${MAIN_LIB_DIR}/mapping/CCampaignHandler.h ${MAIN_LIB_DIR}/mapping/CDrawRoadsOperation.h @@ -364,6 +365,12 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/registerTypes/RegisterTypes.h + ${MAIN_LIB_DIR}/rewardable/Configuration.h + ${MAIN_LIB_DIR}/rewardable/Info.h + ${MAIN_LIB_DIR}/rewardable/Interface.h + ${MAIN_LIB_DIR}/rewardable/Limiter.h + ${MAIN_LIB_DIR}/rewardable/Reward.h + ${MAIN_LIB_DIR}/rmg/RmgArea.h ${MAIN_LIB_DIR}/rmg/RmgObject.h ${MAIN_LIB_DIR}/rmg/RmgPath.h @@ -480,6 +487,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/Interprocess.h ${MAIN_LIB_DIR}/JsonDetail.h ${MAIN_LIB_DIR}/JsonNode.h + ${MAIN_LIB_DIR}/JsonRandom.h ${MAIN_LIB_DIR}/Languages.h ${MAIN_LIB_DIR}/LoadProgress.h ${MAIN_LIB_DIR}/LogicalExpression.h diff --git a/lib/mapObjects/JsonRandom.cpp b/lib/JsonRandom.cpp similarity index 96% rename from lib/mapObjects/JsonRandom.cpp rename to lib/JsonRandom.cpp index 9df0770ec..827ed0bc3 100644 --- a/lib/mapObjects/JsonRandom.cpp +++ b/lib/JsonRandom.cpp @@ -11,18 +11,18 @@ #include "StdInc.h" #include "JsonRandom.h" -#include "../JsonNode.h" -#include "../CRandomGenerator.h" -#include "../StringConstants.h" -#include "../VCMI_Lib.h" -#include "../CModHandler.h" -#include "../CArtHandler.h" -#include "../CCreatureHandler.h" -#include "../CCreatureSet.h" -#include "../spells/CSpellHandler.h" -#include "../CSkillHandler.h" -#include "../mapObjects/CObjectHandler.h" -#include "../IGameCallback.h" +#include "JsonNode.h" +#include "CRandomGenerator.h" +#include "StringConstants.h" +#include "VCMI_Lib.h" +#include "CModHandler.h" +#include "CArtHandler.h" +#include "CCreatureHandler.h" +#include "CCreatureSet.h" +#include "spells/CSpellHandler.h" +#include "CSkillHandler.h" +#include "mapObjects/CObjectHandler.h" +#include "IGameCallback.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/JsonRandom.h b/lib/JsonRandom.h similarity index 97% rename from lib/mapObjects/JsonRandom.h rename to lib/JsonRandom.h index 8c7ee02f4..2ac9e1d65 100644 --- a/lib/mapObjects/JsonRandom.h +++ b/lib/JsonRandom.h @@ -9,8 +9,8 @@ */ #pragma once -#include "../GameConstants.h" -#include "../ResourceSet.h" +#include "GameConstants.h" +#include "ResourceSet.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CRewardableConstructor.cpp b/lib/mapObjects/CRewardableConstructor.cpp index 9bd0eecc1..cf46d9fb1 100644 --- a/lib/mapObjects/CRewardableConstructor.cpp +++ b/lib/mapObjects/CRewardableConstructor.cpp @@ -43,8 +43,8 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG { if(auto * rewardableObject = dynamic_cast(object)) { - objectInfo.configureObject(rewardableObject->configuration(), rng); - for(auto & rewardInfo : rewardableObject->configuration().info) + objectInfo.configureObject(rewardableObject->configuration, rng); + for(auto & rewardInfo : rewardableObject->configuration.info) { for (auto & bonus : rewardInfo.reward.bonuses) { @@ -62,7 +62,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG std::unique_ptr CRewardableConstructor::getObjectInfo(std::shared_ptr tmpl) const { - return std::unique_ptr(new CRandomRewardObjectInfo(objectInfo)); + return std::unique_ptr(new Rewardable::Info(objectInfo)); } VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CRewardableConstructor.h b/lib/mapObjects/CRewardableConstructor.h index 79ca66109..3738c8a0a 100644 --- a/lib/mapObjects/CRewardableConstructor.h +++ b/lib/mapObjects/CRewardableConstructor.h @@ -9,14 +9,14 @@ */ #pragma once -#include "CObjectClassesHandler.h" -#include "CRandomRewardObjectInfo.h" +#include "../mapObjects/CObjectClassesHandler.h" +#include "../rewardable/Info.h" VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler { - CRandomRewardObjectInfo objectInfo; + Rewardable::Info objectInfo; void initTypeData(const JsonNode & config) override; diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 1ddd18534..50481076a 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -10,8 +10,7 @@ #include "StdInc.h" #include "CRewardableObject.h" -#include "CObjectClassesHandler.h" -#include "Rewardable.h" +#include "../mapObjects/CObjectClassesHandler.h" #include "../CGameState.h" #include "../CGeneralTextHandler.h" #include "../CPlayerState.h" @@ -31,16 +30,16 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const { auto grantRewardWithMessage = [&](int index, bool markAsVisit) -> void { - auto vi = getConfiguration().info.at(index); + auto vi = configuration.info.at(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 (getConfiguration().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 = getConfiguration().infoWindowType; + iw.type = configuration.infoWindowType; if(!iw.components.empty() || !iw.text.toString().empty()) cb->showInfoDialog(&iw); } @@ -51,27 +50,27 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const }; auto selectRewardsMessage = [&](const std::vector & rewards, const MetaString & dialog) -> void { - BlockingDialog sd(getConfiguration().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(getConfiguration().info.at(index).reward.getDisplayedComponent(h)); + sd.components.push_back(configuration.info.at(index).reward.getDisplayedComponent(h)); if (rewards.size() == 1) - getConfiguration().info.at(rewards.front()).reward.loadComponents(sd.components, h); + configuration.info.at(rewards.front()).reward.loadComponents(sd.components, h); cb->showBlockingDialog(&sd); }; if(!wasVisitedBefore(h)) { - auto rewards = getAvailableRewards(h, CRewardVisitInfo::EVENT_FIRST_VISIT); + auto rewards = getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT); bool objectRemovalPossible = false; for(auto index : rewards) { - if(getConfiguration().info.at(index).reward.removeObject) + if(configuration.info.at(index).reward.removeObject) objectRemovalPossible = true; } @@ -80,7 +79,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 = getAvailableRewards(h, Rewardable::EEventType::EVENT_NOT_AVAILABLE); if (!emptyRewards.empty()) grantRewardWithMessage(emptyRewards[0], false); else @@ -89,17 +88,17 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const } case 1: // one reward. Just give it with message { - if (getConfiguration().canRefuse) - selectRewardsMessage(rewards, getConfiguration().info.at(rewards.front()).message); + if (configuration.canRefuse) + selectRewardsMessage(rewards, configuration.info.at(rewards.front()).message); else grantRewardWithMessage(rewards.front(), true); break; } default: // multiple rewards. Act according to select mode { - switch (getConfiguration().selectMode) { + switch (configuration.selectMode) { case Rewardable::SELECT_PLAYER: // player must select - selectRewardsMessage(rewards, getConfiguration().onSelect); + selectRewardsMessage(rewards, configuration.onSelect); break; case Rewardable::SELECT_FIRST: // give first available grantRewardWithMessage(rewards.front(), true); @@ -112,7 +111,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const } } - if(!objectRemovalPossible && getAvailableRewards(h, CRewardVisitInfo::EVENT_FIRST_VISIT).empty()) + if(!objectRemovalPossible && getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT).empty()) { ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, h->id); cb->sendAndApply(&cov); @@ -122,7 +121,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const { logGlobal->debug("Revisiting already visited object"); - auto visitedRewards = getAvailableRewards(h, CRewardVisitInfo::EVENT_ALREADY_VISITED); + auto visitedRewards = getAvailableRewards(h, Rewardable::EEventType::EVENT_ALREADY_VISITED); if (!visitedRewards.empty()) grantRewardWithMessage(visitedRewards[0], false); else @@ -132,7 +131,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const void CRewardableObject::heroLevelUpDone(const CGHeroInstance *hero) const { - grantRewardAfterLevelup(cb, getConfiguration().info.at(selectedReward), this, hero); + grantRewardAfterLevelup(cb, configuration.info.at(selectedReward), this, hero); } void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const @@ -140,9 +139,9 @@ void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32 if(answer == 0) return; // player refused - if(answer > 0 && answer-1 < getConfiguration().info.size()) + if(answer > 0 && answer-1 < configuration.info.size()) { - auto list = getAvailableRewards(hero, CRewardVisitInfo::EVENT_FIRST_VISIT); + auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT); markAsVisited(hero); grantReward(list[answer - 1], hero); } @@ -163,18 +162,18 @@ void CRewardableObject::markAsVisited(const CGHeroInstance * hero) const void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero) const { cb->setObjProperty(id, ObjProperty::REWARD_SELECT, rewardID); - grantRewardBeforeLevelup(cb, getConfiguration().info.at(rewardID), hero); + grantRewardBeforeLevelup(cb, configuration.info.at(rewardID), hero); // hero is not blocked by levelup dialog - grant remainer immediately if(!cb->isVisitCoveredByAnotherQuery(this, hero)) { - grantRewardAfterLevelup(cb, getConfiguration().info.at(rewardID), this, hero); + grantRewardAfterLevelup(cb, configuration.info.at(rewardID), this, hero); } } bool CRewardableObject::wasVisitedBefore(const CGHeroInstance * contextHero) const { - switch (getConfiguration().visitMode) + switch (configuration.visitMode) { case Rewardable::VISIT_UNLIMITED: return false; @@ -193,7 +192,7 @@ bool CRewardableObject::wasVisitedBefore(const CGHeroInstance * contextHero) con bool CRewardableObject::wasVisited(PlayerColor player) const { - switch (getConfiguration().visitMode) + switch (configuration.visitMode) { case Rewardable::VISIT_UNLIMITED: case Rewardable::VISIT_BONUS: @@ -209,7 +208,7 @@ bool CRewardableObject::wasVisited(PlayerColor player) const bool CRewardableObject::wasVisited(const CGHeroInstance * h) const { - switch (getConfiguration().visitMode) + switch (configuration.visitMode) { case Rewardable::VISIT_BONUS: return h->hasBonusFrom(Bonus::OBJECT, ID); @@ -222,14 +221,14 @@ bool CRewardableObject::wasVisited(const CGHeroInstance * h) const std::string CRewardableObject::getHoverText(PlayerColor player) const { - if(getConfiguration().visitMode == Rewardable::VISIT_PLAYER || getConfiguration().visitMode == Rewardable::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(getConfiguration().visitMode != Rewardable::VISIT_UNLIMITED) + if(configuration.visitMode != Rewardable::VISIT_UNLIMITED) return getObjectName() + " " + visitedTxt(wasVisited(hero)); return getObjectName(); } @@ -252,13 +251,13 @@ void CRewardableObject::setPropertyDer(ui8 what, ui32 val) void CRewardableObject::newTurn(CRandomGenerator & rand) const { - if (getConfiguration().resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % getConfiguration().resetParameters.period) == 0) + if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0) { - if (getConfiguration().resetParameters.rewards) + if (configuration.resetParameters.rewards) { cb->setObjProperty(id, ObjProperty::REWARD_RANDOMIZE, 0); } - if (getConfiguration().resetParameters.visitors) + if (configuration.resetParameters.visitors) { cb->setObjProperty(id, ObjProperty::REWARD_CLEARED, false); ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_CLEAR, id); @@ -270,7 +269,7 @@ void CRewardableObject::newTurn(CRandomGenerator & rand) const void CRewardableObject::initObj(CRandomGenerator & rand) { VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); - assert(!getConfiguration().info.empty()); + assert(!configuration.info.empty()); } CRewardableObject::CRewardableObject() diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 825339245..ca8f918db 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -9,8 +9,8 @@ */ #pragma once -#include "CArmedInstance.h" -#include "Rewardable.h" +#include "../mapObjects/CArmedInstance.h" +#include "../rewardable/Interface.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/Rewardable.cpp b/lib/mapObjects/Rewardable.cpp deleted file mode 100644 index c4440eeb6..000000000 --- a/lib/mapObjects/Rewardable.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Rewardable.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 "Rewardable.h" - -#include "../CHeroHandler.h" -#include "../CSoundBase.h" -#include "../NetPacks.h" -#include "../IGameCallback.h" -#include "../CPlayerState.h" -#include "../spells/CSpellHandler.h" -#include "../spells/ISpellMechanics.h" -#include "../mapObjects/MiscObjects.h" - -VCMI_LIB_NAMESPACE_BEGIN - -bool CRewardLimiter::heroAllowed(const CGHeroInstance * hero) const -{ - if(dayOfWeek != 0) - { - if (IObjectInterface::cb->getDate(Date::DAY_OF_WEEK) != dayOfWeek) - return false; - } - - if(daysPassed != 0) - { - if (IObjectInterface::cb->getDate(Date::DAY) < daysPassed) - return false; - } - - for(const auto & reqStack : creatures) - { - size_t count = 0; - for(const auto & slot : hero->Slots()) - { - const CStackInstance * heroStack = slot.second; - if (heroStack->type == reqStack.type) - count += heroStack->count; - } - if (count < reqStack.count) //not enough creatures of this kind - return false; - } - - if(!IObjectInterface::cb->getPlayerState(hero->tempOwner)->resources.canAfford(resources)) - return false; - - if(heroLevel > static_cast(hero->level)) - return false; - - if(static_cast(heroExperience) > hero->exp) - return false; - - if(manaPoints > hero->mana) - return false; - - if(manaPercentage > 100 * hero->mana / hero->manaLimit()) - return false; - - for(size_t i=0; i hero->getPrimSkillLevel(static_cast(i))) - return false; - } - - for(const auto & skill : secondary) - { - if (skill.second > hero->getSecSkillLevel(skill.first)) - return false; - } - - for(const auto & spell : spells) - { - if (!hero->spellbookContainsSpell(spell)) - return false; - } - - for(const auto & art : artifacts) - { - if (!hero->hasArt(art)) - return false; - } - - for(const auto & sublimiter : noneOf) - { - if (sublimiter->heroAllowed(hero)) - return false; - } - - for(const auto & sublimiter : allOf) - { - if (!sublimiter->heroAllowed(hero)) - return false; - } - - if(anyOf.empty()) - return true; - - for(const auto & sublimiter : anyOf) - { - if (sublimiter->heroAllowed(hero)) - return true; - } - return false; -} - -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); - } -} - -std::vector Rewardable::Interface::getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event) const -{ - std::vector ret; - - for(size_t i = 0; i < _configuration.info.size(); i++) - { - const CRewardVisitInfo & visit = _configuration.info[i]; - - if(event == visit.visitType && visit.limiter.heroAllowed(hero)) - { - logGlobal->trace("Reward %d is allowed", i); - ret.push_back(static_cast(i)); - } - } - return ret; -} - -Rewardable::EVisitMode Rewardable::Configuration::getVisitMode() const -{ - return static_cast(visitMode); -} - -ui16 Rewardable::Configuration::getResetDuration() const -{ - return resetParameters.period; -} - -const Rewardable::Configuration & Rewardable::Interface::getConfiguration() const -{ - return _configuration; -} - -Rewardable::Configuration & Rewardable::Interface::configuration() -{ - return _configuration; -} - -void Rewardable::Interface::grantRewardBeforeLevelup(IGameCallback * cb, const CRewardVisitInfo & info, const CGHeroInstance * hero) const -{ - assert(hero); - assert(hero->tempOwner.isValidPlayer()); - assert(info.reward.creatures.size() <= GameConstants::ARMY_SIZE); - - cb->giveResources(hero->tempOwner, info.reward.resources); - - for(const auto & entry : info.reward.secondary) - { - int current = hero->getSecSkillLevel(entry.first); - if( (current != 0 && current < entry.second) || - (hero->canLearnSkill() )) - { - cb->changeSecSkill(hero, entry.first, entry.second); - } - } - - for(int i=0; i< info.reward.primary.size(); i++) - cb->changePrimSkill(hero, static_cast(i), info.reward.primary[i], false); - - si64 expToGive = 0; - - if (info.reward.heroLevel > 0) - expToGive += VLC->heroh->reqExp(hero->level+info.reward.heroLevel) - VLC->heroh->reqExp(hero->level); - - if (info.reward.heroExperience > 0) - expToGive += hero->calculateXp(info.reward.heroExperience); - - if(expToGive) - cb->changePrimSkill(hero, PrimarySkill::EXPERIENCE, expToGive); -} - -void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const CRewardVisitInfo & info, const CArmedInstance * army, const CGHeroInstance * hero) const -{ - if(info.reward.manaDiff || info.reward.manaPercentage >= 0) - cb->setManaPoints(hero->id, info.reward.calculateManaPoints(hero)); - - if(info.reward.movePoints || info.reward.movePercentage >= 0) - { - SetMovePoints smp; - smp.hid = hero->id; - smp.val = hero->movement; - - if (info.reward.movePercentage >= 0) // percent from max - smp.val = hero->maxMovePoints(hero->boat && hero->boat->layer == EPathfindingLayer::SAIL) * info.reward.movePercentage / 100; - smp.val = std::max(0, smp.val + info.reward.movePoints); - - cb->setMovePoints(&smp); - } - - for(const Bonus & bonus : info.reward.bonuses) - { - assert(bonus.source == Bonus::OBJECT); - GiveBonus gb; - gb.who = GiveBonus::ETarget::HERO; - gb.bonus = bonus; - gb.id = hero->id.getNum(); - cb->giveHeroBonus(&gb); - } - - for(const ArtifactID & art : info.reward.artifacts) - cb->giveHeroNewArtifact(hero, VLC->arth->objects[art],ArtifactPosition::FIRST_AVAILABLE); - - if(!info.reward.spells.empty()) - { - std::set spellsToGive(info.reward.spells.begin(), info.reward.spells.end()); - cb->changeSpells(hero, true, spellsToGive); - } - - if(!info.reward.creaturesChange.empty()) - { - for(const auto & slot : hero->Slots()) - { - const CStackInstance * heroStack = slot.second; - - for(const auto & change : info.reward.creaturesChange) - { - if (heroStack->type->getId() == change.first) - { - StackLocation location(hero, slot.first); - cb->changeStackType(location, change.second.toCreature()); - break; - } - } - } - } - - if(!info.reward.creatures.empty()) - { - CCreatureSet creatures; - for(const auto & crea : info.reward.creatures) - creatures.addToSlot(creatures.getFreeSlot(), new CStackInstance(crea.type, crea.count)); - - if(auto * army = dynamic_cast(this)) //TODO: to fix that, CArmedInstance must be splitted on map instance part and interface part - cb->giveCreatures(army, hero, creatures, false); - } - - if(info.reward.spellCast.first != SpellID::NONE) - { - caster.setActualCaster(hero); - caster.setSpellSchoolLevel(info.reward.spellCast.second); - cb->castSpell(&caster, info.reward.spellCast.first, int3{-1, -1, -1}); - - if(info.reward.removeObject) - logMod->warn("Removal of object with spell casts is not supported!"); - } - else if(info.reward.removeObject) //FIXME: object can't track spell cancel or finish, so removeObject leads to crash - if(auto * instance = dynamic_cast(this)) - cb->removeObject(instance); -} - -VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/Rewardable.h b/lib/mapObjects/Rewardable.h deleted file mode 100644 index d57fdb058..000000000 --- a/lib/mapObjects/Rewardable.h +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Rewardable.h, 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 - * - */ - -#pragma once - -#include "CObjectHandler.h" -#include "../CCreatureSet.h" -#include "../ResourceSet.h" -#include "../spells/ExternalCaster.h" - -VCMI_LIB_NAMESPACE_BEGIN - -class CRandomRewardObjectInfo; - -class CRewardLimiter; -using TRewardLimitersList = std::vector>; - -/// Limiters of rewards. Rewards will be granted to hero only if he satisfies requirements -/// Note: for this is only a test - it won't remove anything from hero (e.g. artifacts or creatures) -/// NOTE: in future should (partially) replace seer hut/quest guard quests checks -class DLL_LINKAGE CRewardLimiter -{ -public: - /// day of week, unused if 0, 1-7 will test for current day of week - si32 dayOfWeek; - si32 daysPassed; - - /// total experience that hero needs to have - si32 heroExperience; - - /// level that hero needs to have - si32 heroLevel; - - /// mana points that hero needs to have - si32 manaPoints; - - /// percentage of mana points that hero needs to have - si32 manaPercentage; - - /// resources player needs to have in order to trigger reward - TResources resources; - - /// skills hero needs to have - std::vector primary; - std::map secondary; - - /// artifacts that hero needs to have (equipped or in backpack) to trigger this - /// Note: does not checks for multiple copies of the same arts - std::vector artifacts; - - /// Spells that hero must have in the spellbook - std::vector spells; - - /// creatures that hero needs to have - std::vector creatures; - - /// sub-limiters, all must pass for this limiter to pass - TRewardLimitersList allOf; - - /// sub-limiters, at least one should pass for this limiter to pass - TRewardLimitersList anyOf; - - /// sub-limiters, none should pass for this limiter to pass - TRewardLimitersList noneOf; - - CRewardLimiter(): - dayOfWeek(0), - daysPassed(0), - heroExperience(0), - heroLevel(0), - manaPercentage(0), - manaPoints(0), - primary(GameConstants::PRIMARY_SKILLS, 0) - {} - - bool heroAllowed(const CGHeroInstance * hero) const; - - template void serialize(Handler &h, const int version) - { - h & dayOfWeek; - h & daysPassed; - h & heroExperience; - h & heroLevel; - h & manaPoints; - h & manaPercentage; - h & resources; - h & primary; - h & secondary; - h & artifacts; - h & creatures; - h & allOf; - h & anyOf; - h & noneOf; - } -}; - -class DLL_LINKAGE CRewardResetInfo -{ -public: - CRewardResetInfo() - : period(0) - , visitors(false) - , rewards(false) - {} - - /// if above zero, object state will be reset each resetDuration days - ui32 period; - - /// if true - reset list of visitors (heroes & players) on reset - bool visitors; - - - /// if true - re-randomize rewards on a new week - bool rewards; - - template void serialize(Handler &h, const int version) - { - h & period; - h & visitors; - h & rewards; - } -}; - -/// Reward that can be granted to a hero -/// NOTE: eventually should replace seer hut rewards and events/pandoras -class DLL_LINKAGE CRewardInfo -{ -public: - /// resources that will be given to player - TResources resources; - - /// received experience - si32 heroExperience; - /// received levels (converted into XP during grant) - si32 heroLevel; - - /// mana given to/taken from hero, fixed value - si32 manaDiff; - - /// if giving mana points puts hero above mana pool, any overflow will be multiplied by specified percentage - si32 manaOverflowFactor; - - /// fixed value, in form of percentage from max - si32 manaPercentage; - - /// movement points, only for current day. Bonuses should be used to grant MP on any other day - si32 movePoints; - /// fixed value, in form of percentage from max - si32 movePercentage; - - /// list of bonuses, e.g. morale/luck - std::vector bonuses; - - /// skills that hero may receive or lose - std::vector primary; - std::map secondary; - - /// creatures that will be changed in hero's army - std::map creaturesChange; - - /// objects that hero may receive - std::vector artifacts; - std::vector spells; - std::vector creatures; - - /// actions that hero may execute and object caster. Pair of spellID and school level - std::pair spellCast; - - /// list of components that will be added to reward description. First entry in list will override displayed component - std::vector extraComponents; - - /// if set to true, object will be removed after granting reward - bool removeObject; - - /// 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; - - CRewardInfo() : - heroExperience(0), - heroLevel(0), - manaDiff(0), - manaPercentage(-1), - movePoints(0), - movePercentage(-1), - primary(4, 0), - removeObject(false), - spellCast(SpellID::NONE, SecSkillLevel::NONE) - {} - - template void serialize(Handler &h, const int version) - { - h & resources; - h & extraComponents; - h & removeObject; - h & manaPercentage; - h & movePercentage; - h & heroExperience; - h & heroLevel; - h & manaDiff; - h & manaOverflowFactor; - h & movePoints; - h & primary; - h & secondary; - h & bonuses; - h & artifacts; - h & spells; - h & creatures; - h & creaturesChange; - if(version >= 821) - h & spellCast; - } -}; - -class DLL_LINKAGE CRewardVisitInfo -{ -public: - enum ERewardEventType - { - EVENT_INVALID, - EVENT_FIRST_VISIT, - EVENT_ALREADY_VISITED, - EVENT_NOT_AVAILABLE - }; - - CRewardLimiter limiter; - CRewardInfo reward; - - /// Message that will be displayed on granting of this reward, if not empty - MetaString message; - - /// Event to which this reward is assigned - ERewardEventType visitType; - - CRewardVisitInfo() = default; - - template void serialize(Handler &h, const int version) - { - h & limiter; - h & reward; - h & message; - h & visitType; - } -}; - -namespace Rewardable -{ - enum EVisitMode - { - VISIT_UNLIMITED, // any number of times. Side effect - object hover text won't contain visited/not visited text - VISIT_ONCE, // only once, first to visit get all the rewards - VISIT_HERO, // every hero can visit object once - VISIT_BONUS, // can be visited by any hero that don't have bonus from this object - VISIT_PLAYER // every player can visit object once - }; - - /// controls selection of reward granted to player - enum ESelectMode - { - SELECT_FIRST, // first reward that matches limiters - SELECT_PLAYER, // player can select from all allowed rewards - SELECT_RANDOM, // one random reward from all mathing limiters - }; - - const std::array SelectModeString{"selectFirst", "selectPlayer", "selectRandom"}; - const std::array VisitModeString{"unlimited", "once", "hero", "bonus", "player"}; - - /// Base class that can handle granting rewards to visiting heroes. - struct DLL_LINKAGE Configuration - { - /// Message that will be shown if player needs to select one of multiple rewards - MetaString onSelect; - - /// Rewards that can be applied by an object - std::vector info; - - /// how reward will be selected, uses ESelectMode enum - ui8 selectMode = Rewardable::SELECT_FIRST; - - /// contols who can visit an object, uses EVisitMode enum - ui8 visitMode = Rewardable::VISIT_UNLIMITED; - - /// how and when should the object be reset - CRewardResetInfo resetParameters; - - /// if true - player can refuse visiting an object (e.g. Tomb) - bool canRefuse = false; - - /// if true - object info will shown in infobox (like resource pickup) - EInfoWindowMode infoWindowType = EInfoWindowMode::AUTO; - - EVisitMode getVisitMode() const; - ui16 getResetDuration() const; - - template void serialize(Handler &h, const int version) - { - h & info; - h & canRefuse; - h & resetParameters; - h & onSelect; - h & visitMode; - h & selectMode; - h & infoWindowType; - } - }; - - class DLL_LINKAGE Interface - { - private: - - Rewardable::Configuration _configuration; - - /// caster to cast adveture spells, no serialize - mutable spells::ExternalCaster caster; - - protected: - - /// filters list of visit info and returns rewards that can be granted to current hero - std::vector getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event) const; - - /// function that must be called if hero got level-up during grantReward call - virtual void grantRewardAfterLevelup(IGameCallback * cb, const CRewardVisitInfo & reward, const CArmedInstance * army, const CGHeroInstance * hero) const; - - /// grants reward to hero - virtual void grantRewardBeforeLevelup(IGameCallback * cb, const CRewardVisitInfo & reward, const CGHeroInstance * hero) const; - - public: - - const Rewardable::Configuration & getConfiguration() const; - Rewardable::Configuration & configuration(); - - template void serialize(Handler &h, const int version) - { - h & _configuration; - } - }; -} - -VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Configuration.cpp b/lib/rewardable/Configuration.cpp new file mode 100644 index 000000000..a6175f356 --- /dev/null +++ b/lib/rewardable/Configuration.cpp @@ -0,0 +1,25 @@ +/* + * Configuration.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 "Configuration.h" + +VCMI_LIB_NAMESPACE_BEGIN + +Rewardable::EVisitMode Rewardable::Configuration::getVisitMode() const +{ + return static_cast(visitMode); +} + +ui16 Rewardable::Configuration::getResetDuration() const +{ + return resetParameters.period; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Configuration.h b/lib/rewardable/Configuration.h new file mode 100644 index 000000000..5d00ad4ca --- /dev/null +++ b/lib/rewardable/Configuration.h @@ -0,0 +1,136 @@ +/* + * Configuration.h, 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 + * + */ + +#pragma once + +#include "Limiter.h" +#include "Reward.h" + +VCMI_LIB_NAMESPACE_BEGIN + +namespace Rewardable +{ + +enum EVisitMode +{ + VISIT_UNLIMITED, // any number of times. Side effect - object hover text won't contain visited/not visited text + VISIT_ONCE, // only once, first to visit get all the rewards + VISIT_HERO, // every hero can visit object once + VISIT_BONUS, // can be visited by any hero that don't have bonus from this object + VISIT_PLAYER // every player can visit object once +}; + +/// controls selection of reward granted to player +enum ESelectMode +{ + SELECT_FIRST, // first reward that matches limiters + SELECT_PLAYER, // player can select from all allowed rewards + SELECT_RANDOM, // one random reward from all mathing limiters +}; + +enum class EEventType +{ + EVENT_INVALID = 0, + EVENT_FIRST_VISIT, + EVENT_ALREADY_VISITED, + EVENT_NOT_AVAILABLE +}; + +const std::array SelectModeString{"selectFirst", "selectPlayer", "selectRandom"}; +const std::array VisitModeString{"unlimited", "once", "hero", "bonus", "player"}; + +struct DLL_LINKAGE ResetInfo +{ + ResetInfo() + : period(0) + , visitors(false) + , rewards(false) + {} + + /// if above zero, object state will be reset each resetDuration days + ui32 period; + + /// if true - reset list of visitors (heroes & players) on reset + bool visitors; + + + /// if true - re-randomize rewards on a new week + bool rewards; + + template void serialize(Handler &h, const int version) + { + h & period; + h & visitors; + h & rewards; + } +}; + +struct DLL_LINKAGE VisitInfo +{ + Limiter limiter; + Reward reward; + + /// Message that will be displayed on granting of this reward, if not empty + MetaString message; + + /// Event to which this reward is assigned + EEventType visitType; + + template void serialize(Handler &h, const int version) + { + h & limiter; + h & reward; + h & message; + h & visitType; + } +}; + +/// Base class that can handle granting rewards to visiting heroes. +struct DLL_LINKAGE Configuration +{ + /// Message that will be shown if player needs to select one of multiple rewards + MetaString onSelect; + + /// Rewards that can be applied by an object + std::vector info; + + /// how reward will be selected, uses ESelectMode enum + ui8 selectMode = Rewardable::SELECT_FIRST; + + /// contols who can visit an object, uses EVisitMode enum + ui8 visitMode = Rewardable::VISIT_UNLIMITED; + + /// how and when should the object be reset + Rewardable::ResetInfo resetParameters; + + /// if true - player can refuse visiting an object (e.g. Tomb) + bool canRefuse = false; + + /// if true - object info will shown in infobox (like resource pickup) + EInfoWindowMode infoWindowType = EInfoWindowMode::AUTO; + + EVisitMode getVisitMode() const; + ui16 getResetDuration() const; + + template void serialize(Handler &h, const int version) + { + h & info; + h & canRefuse; + h & resetParameters; + h & onSelect; + h & visitMode; + h & selectMode; + h & infoWindowType; + } +}; + +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CRandomRewardObjectInfo.cpp b/lib/rewardable/Info.cpp similarity index 79% rename from lib/mapObjects/CRandomRewardObjectInfo.cpp rename to lib/rewardable/Info.cpp index 6e7657095..37fb2d57d 100644 --- a/lib/mapObjects/CRandomRewardObjectInfo.cpp +++ b/lib/rewardable/Info.cpp @@ -1,5 +1,5 @@ /* - * CRandomRewardObjectInfo.cpp, part of VCMI engine + * Info.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * @@ -9,15 +9,21 @@ */ #include "StdInc.h" -#include "CRandomRewardObjectInfo.h" +#include "Info.h" +#include "Limiter.h" +#include "Reward.h" +#include "Configuration.h" #include "../CRandomGenerator.h" #include "../StringConstants.h" #include "../CCreatureHandler.h" #include "../CModHandler.h" -#include "JsonRandom.h" +#include "../JsonRandom.h" #include "../IGameCallback.h" #include "../CGeneralTextHandler.h" +#include "../JsonNode.h" +#include "../IGameCallback.h" +#include "../CPlayerState.h" VCMI_LIB_NAMESPACE_BEGIN @@ -43,17 +49,17 @@ namespace { } } -void CRandomRewardObjectInfo::init(const JsonNode & objectConfig) +void Rewardable::Info::init(const JsonNode & objectConfig) { parameters = objectConfig; } -TRewardLimitersList CRandomRewardObjectInfo::configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const +Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const { - TRewardLimitersList result; + Rewardable::LimitersList result; for (const auto & input : source.Vector()) { - auto newLimiter = std::make_shared(); + auto newLimiter = std::make_shared(); configureLimiter(object, rng, *newLimiter, input); @@ -63,7 +69,7 @@ TRewardLimitersList CRandomRewardObjectInfo::configureSublimiters(Rewardable::Co return result; } -void CRandomRewardObjectInfo::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardLimiter & limiter, const JsonNode & source) const +void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const { std::vector spells; for (size_t i=0; i<6; i++) @@ -92,7 +98,7 @@ void CRandomRewardObjectInfo::configureLimiter(Rewardable::Configuration & objec limiter.noneOf = configureSublimiters(object, rng, source["noneOf"] ); } -void CRandomRewardObjectInfo::configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardInfo & reward, const JsonNode & source) const +void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Reward & reward, const JsonNode & source) const { reward.resources = JsonRandom::loadResources(source["resources"], rng); @@ -139,19 +145,19 @@ void CRandomRewardObjectInfo::configureReward(Rewardable::Configuration & object } } -void CRandomRewardObjectInfo::configureResetInfo(Rewardable::Configuration & object, CRandomGenerator & rng, CRewardResetInfo & resetParameters, const JsonNode & source) const +void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::ResetInfo & resetParameters, const JsonNode & source) const { resetParameters.period = static_cast(source["period"].Float()); resetParameters.visitors = source["visitors"].Bool(); resetParameters.rewards = source["rewards"].Bool(); } -void CRandomRewardObjectInfo::configureRewards( +void Rewardable::Info::configureRewards( Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source, std::map & thrownDice, - CRewardVisitInfo::ERewardEventType event ) const + Rewardable::EEventType event ) const { for (const JsonNode & reward : source.Vector()) { @@ -177,7 +183,7 @@ void CRandomRewardObjectInfo::configureRewards( } } - CRewardVisitInfo info; + Rewardable::VisitInfo info; configureLimiter(object, rng, info.limiter, reward["limiter"]); configureReward(object, rng, info.reward, reward); @@ -194,30 +200,30 @@ void CRandomRewardObjectInfo::configureRewards( } } -void CRandomRewardObjectInfo::configureObject(Rewardable::Configuration & object, CRandomGenerator & rng) const +void Rewardable::Info::configureObject(Rewardable::Configuration & object, CRandomGenerator & rng) const { object.info.clear(); std::map thrownDice; - configureRewards(object, rng, parameters["rewards"], thrownDice, CRewardVisitInfo::EVENT_FIRST_VISIT); - configureRewards(object, rng, parameters["onVisited"], thrownDice, CRewardVisitInfo::EVENT_ALREADY_VISITED); - configureRewards(object, rng, parameters["onEmpty"], thrownDice, CRewardVisitInfo::EVENT_NOT_AVAILABLE); + configureRewards(object, rng, parameters["rewards"], thrownDice, Rewardable::EEventType::EVENT_FIRST_VISIT); + configureRewards(object, rng, parameters["onVisited"], thrownDice, Rewardable::EEventType::EVENT_ALREADY_VISITED); + configureRewards(object, rng, parameters["onEmpty"], thrownDice, Rewardable::EEventType::EVENT_NOT_AVAILABLE); object.onSelect = loadMessage(parameters["onSelectMessage"]); if (!parameters["onVisitedMessage"].isNull()) { - CRewardVisitInfo onVisited; - onVisited.visitType = CRewardVisitInfo::EVENT_ALREADY_VISITED; + Rewardable::VisitInfo onVisited; + onVisited.visitType = Rewardable::EEventType::EVENT_ALREADY_VISITED; onVisited.message = loadMessage(parameters["onVisitedMessage"]); object.info.push_back(onVisited); } if (!parameters["onEmptyMessage"].isNull()) { - CRewardVisitInfo onEmpty; - onEmpty.visitType = CRewardVisitInfo::EVENT_NOT_AVAILABLE; + Rewardable::VisitInfo onEmpty; + onEmpty.visitType = Rewardable::EEventType::EVENT_NOT_AVAILABLE; onEmpty.message = loadMessage(parameters["onEmptyMessage"]); object.info.push_back(onEmpty); } @@ -252,57 +258,57 @@ void CRandomRewardObjectInfo::configureObject(Rewardable::Configuration & object } } -bool CRandomRewardObjectInfo::givesResources() const +bool Rewardable::Info::givesResources() const { return testForKey(parameters, "resources"); } -bool CRandomRewardObjectInfo::givesExperience() const +bool Rewardable::Info::givesExperience() const { return testForKey(parameters, "gainedExp") || testForKey(parameters, "gainedLevels"); } -bool CRandomRewardObjectInfo::givesMana() const +bool Rewardable::Info::givesMana() const { return testForKey(parameters, "manaPoints") || testForKey(parameters, "manaPercentage"); } -bool CRandomRewardObjectInfo::givesMovement() const +bool Rewardable::Info::givesMovement() const { return testForKey(parameters, "movePoints") || testForKey(parameters, "movePercentage"); } -bool CRandomRewardObjectInfo::givesPrimarySkills() const +bool Rewardable::Info::givesPrimarySkills() const { return testForKey(parameters, "primary"); } -bool CRandomRewardObjectInfo::givesSecondarySkills() const +bool Rewardable::Info::givesSecondarySkills() const { return testForKey(parameters, "secondary"); } -bool CRandomRewardObjectInfo::givesArtifacts() const +bool Rewardable::Info::givesArtifacts() const { return testForKey(parameters, "artifacts"); } -bool CRandomRewardObjectInfo::givesCreatures() const +bool Rewardable::Info::givesCreatures() const { return testForKey(parameters, "spells"); } -bool CRandomRewardObjectInfo::givesSpells() const +bool Rewardable::Info::givesSpells() const { return testForKey(parameters, "creatures"); } -bool CRandomRewardObjectInfo::givesBonuses() const +bool Rewardable::Info::givesBonuses() const { return testForKey(parameters, "bonuses"); } -const JsonNode & CRandomRewardObjectInfo::getParameters() const +const JsonNode & Rewardable::Info::getParameters() const { return parameters; } diff --git a/lib/mapObjects/CRandomRewardObjectInfo.h b/lib/rewardable/Info.h similarity index 64% rename from lib/mapObjects/CRandomRewardObjectInfo.h rename to lib/rewardable/Info.h index 2e8df527b..c9cca1ca7 100644 --- a/lib/mapObjects/CRandomRewardObjectInfo.h +++ b/lib/rewardable/Info.h @@ -1,5 +1,5 @@ /* - * CRandomRewardObjectInfo.h, part of VCMI engine + * Info.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * @@ -11,24 +11,33 @@ #pragma once #include "../JsonNode.h" -#include "CObjectClassesHandler.h" -#include "Rewardable.h" +#include "../mapObjects/CObjectClassesHandler.h" VCMI_LIB_NAMESPACE_BEGIN class CRandomGenerator; -class DLL_LINKAGE CRandomRewardObjectInfo : public IObjectInfo +namespace Rewardable +{ + +struct Limiter; +using LimitersList = std::vector>; +struct Reward; +struct Configuration; +struct ResetInfo; +enum class EEventType; + +class DLL_LINKAGE Info : public IObjectInfo { JsonNode parameters; - void configureRewards(Rewardable::Configuration & 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, Rewardable::EEventType mode) 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 configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const; + Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, 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; + void configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Reward & info, const JsonNode & source) const; + void configureResetInfo(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::ResetInfo & info, const JsonNode & source) const; public: const JsonNode & getParameters() const; @@ -57,4 +66,6 @@ public: } }; +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Interface.cpp b/lib/rewardable/Interface.cpp new file mode 100644 index 000000000..8e4261c83 --- /dev/null +++ b/lib/rewardable/Interface.cpp @@ -0,0 +1,154 @@ +/* + * Interface.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 "Interface.h" + +#include "../CHeroHandler.h" +#include "../CSoundBase.h" +#include "../NetPacks.h" +#include "../spells/CSpellHandler.h" +#include "../spells/ISpellMechanics.h" +#include "../mapObjects/MiscObjects.h" +#include "../IGameCallback.h" +#include "../CPlayerState.h" + +VCMI_LIB_NAMESPACE_BEGIN + +std::vector Rewardable::Interface::getAvailableRewards(const CGHeroInstance * hero, Rewardable::EEventType event) const +{ + std::vector ret; + + for(size_t i = 0; i < configuration.info.size(); i++) + { + const Rewardable::VisitInfo & visit = configuration.info[i]; + + if(event == visit.visitType && visit.limiter.heroAllowed(hero)) + { + logGlobal->trace("Reward %d is allowed", i); + ret.push_back(static_cast(i)); + } + } + return ret; +} + +void Rewardable::Interface::grantRewardBeforeLevelup(IGameCallback * cb, const Rewardable::VisitInfo & info, const CGHeroInstance * hero) const +{ + assert(hero); + assert(hero->tempOwner.isValidPlayer()); + assert(info.reward.creatures.size() <= GameConstants::ARMY_SIZE); + + cb->giveResources(hero->tempOwner, info.reward.resources); + + for(const auto & entry : info.reward.secondary) + { + int current = hero->getSecSkillLevel(entry.first); + if( (current != 0 && current < entry.second) || + (hero->canLearnSkill() )) + { + cb->changeSecSkill(hero, entry.first, entry.second); + } + } + + for(int i=0; i< info.reward.primary.size(); i++) + cb->changePrimSkill(hero, static_cast(i), info.reward.primary[i], false); + + si64 expToGive = 0; + + if (info.reward.heroLevel > 0) + expToGive += VLC->heroh->reqExp(hero->level+info.reward.heroLevel) - VLC->heroh->reqExp(hero->level); + + if (info.reward.heroExperience > 0) + expToGive += hero->calculateXp(info.reward.heroExperience); + + if(expToGive) + cb->changePrimSkill(hero, PrimarySkill::EXPERIENCE, expToGive); +} + +void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const Rewardable::VisitInfo & info, const CArmedInstance * army, const CGHeroInstance * hero) const +{ + if(info.reward.manaDiff || info.reward.manaPercentage >= 0) + cb->setManaPoints(hero->id, info.reward.calculateManaPoints(hero)); + + if(info.reward.movePoints || info.reward.movePercentage >= 0) + { + SetMovePoints smp; + smp.hid = hero->id; + smp.val = hero->movement; + + if (info.reward.movePercentage >= 0) // percent from max + smp.val = hero->maxMovePoints(hero->boat && hero->boat->layer == EPathfindingLayer::SAIL) * info.reward.movePercentage / 100; + smp.val = std::max(0, smp.val + info.reward.movePoints); + + cb->setMovePoints(&smp); + } + + for(const Bonus & bonus : info.reward.bonuses) + { + assert(bonus.source == Bonus::OBJECT); + GiveBonus gb; + gb.who = GiveBonus::ETarget::HERO; + gb.bonus = bonus; + gb.id = hero->id.getNum(); + cb->giveHeroBonus(&gb); + } + + for(const ArtifactID & art : info.reward.artifacts) + cb->giveHeroNewArtifact(hero, VLC->arth->objects[art],ArtifactPosition::FIRST_AVAILABLE); + + if(!info.reward.spells.empty()) + { + std::set spellsToGive(info.reward.spells.begin(), info.reward.spells.end()); + cb->changeSpells(hero, true, spellsToGive); + } + + if(!info.reward.creaturesChange.empty()) + { + for(const auto & slot : hero->Slots()) + { + const CStackInstance * heroStack = slot.second; + + for(const auto & change : info.reward.creaturesChange) + { + if (heroStack->type->getId() == change.first) + { + StackLocation location(hero, slot.first); + cb->changeStackType(location, change.second.toCreature()); + break; + } + } + } + } + + if(!info.reward.creatures.empty()) + { + CCreatureSet creatures; + for(const auto & crea : info.reward.creatures) + creatures.addToSlot(creatures.getFreeSlot(), new CStackInstance(crea.type, crea.count)); + + if(auto * army = dynamic_cast(this)) //TODO: to fix that, CArmedInstance must be splitted on map instance part and interface part + cb->giveCreatures(army, hero, creatures, false); + } + + if(info.reward.spellCast.first != SpellID::NONE) + { + caster.setActualCaster(hero); + caster.setSpellSchoolLevel(info.reward.spellCast.second); + cb->castSpell(&caster, info.reward.spellCast.first, int3{-1, -1, -1}); + + if(info.reward.removeObject) + logMod->warn("Removal of object with spell casts is not supported!"); + } + else if(info.reward.removeObject) //FIXME: object can't track spell cancel or finish, so removeObject leads to crash + if(auto * instance = dynamic_cast(this)) + cb->removeObject(instance); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Interface.h b/lib/rewardable/Interface.h new file mode 100644 index 000000000..2f8298699 --- /dev/null +++ b/lib/rewardable/Interface.h @@ -0,0 +1,54 @@ +/* + * Interface.h, 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 + * + */ + +#pragma once + +#include "../mapObjects/CObjectHandler.h" +#include "../CCreatureSet.h" +#include "../ResourceSet.h" +#include "../spells/ExternalCaster.h" +#include "Configuration.h" + +VCMI_LIB_NAMESPACE_BEGIN + +namespace Rewardable +{ + +class DLL_LINKAGE Interface +{ +private: + + /// caster to cast adveture spells, no serialize + mutable spells::ExternalCaster caster; + +protected: + + /// filters list of visit info and returns rewards that can be granted to current hero + std::vector getAvailableRewards(const CGHeroInstance * hero, Rewardable::EEventType event) const; + + /// function that must be called if hero got level-up during grantReward call + virtual void grantRewardAfterLevelup(IGameCallback * cb, const Rewardable::VisitInfo & reward, const CArmedInstance * army, const CGHeroInstance * hero) const; + + /// grants reward to hero + virtual void grantRewardBeforeLevelup(IGameCallback * cb, const Rewardable::VisitInfo & reward, const CGHeroInstance * hero) const; + +public: + + Rewardable::Configuration configuration; + + template void serialize(Handler &h, const int version) + { + h & configuration; + } +}; + +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Limiter.cpp b/lib/rewardable/Limiter.cpp new file mode 100644 index 000000000..deb46d30e --- /dev/null +++ b/lib/rewardable/Limiter.cpp @@ -0,0 +1,107 @@ +/* + * Limiter.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 "Limiter.h" +#include "../IGameCallback.h" +#include "../CPlayerState.h" + +VCMI_LIB_NAMESPACE_BEGIN + +bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const +{ + if(dayOfWeek != 0) + { + if (IObjectInterface::cb->getDate(Date::DAY_OF_WEEK) != dayOfWeek) + return false; + } + + if(daysPassed != 0) + { + if (IObjectInterface::cb->getDate(Date::DAY) < daysPassed) + return false; + } + + for(const auto & reqStack : creatures) + { + size_t count = 0; + for(const auto & slot : hero->Slots()) + { + const CStackInstance * heroStack = slot.second; + if (heroStack->type == reqStack.type) + count += heroStack->count; + } + if (count < reqStack.count) //not enough creatures of this kind + return false; + } + + if(!IObjectInterface::cb->getPlayerState(hero->tempOwner)->resources.canAfford(resources)) + return false; + + if(heroLevel > static_cast(hero->level)) + return false; + + if(static_cast(heroExperience) > hero->exp) + return false; + + if(manaPoints > hero->mana) + return false; + + if(manaPercentage > 100 * hero->mana / hero->manaLimit()) + return false; + + for(size_t i=0; i hero->getPrimSkillLevel(static_cast(i))) + return false; + } + + for(const auto & skill : secondary) + { + if (skill.second > hero->getSecSkillLevel(skill.first)) + return false; + } + + for(const auto & spell : spells) + { + if (!hero->spellbookContainsSpell(spell)) + return false; + } + + for(const auto & art : artifacts) + { + if (!hero->hasArt(art)) + return false; + } + + for(const auto & sublimiter : noneOf) + { + if (sublimiter->heroAllowed(hero)) + return false; + } + + for(const auto & sublimiter : allOf) + { + if (!sublimiter->heroAllowed(hero)) + return false; + } + + if(anyOf.empty()) + return true; + + for(const auto & sublimiter : anyOf) + { + if (sublimiter->heroAllowed(hero)) + return true; + } + return false; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Limiter.h b/lib/rewardable/Limiter.h new file mode 100644 index 000000000..46d40a762 --- /dev/null +++ b/lib/rewardable/Limiter.h @@ -0,0 +1,102 @@ +/* + * Limiter.h, 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 + * + */ + +#pragma once + +#include "../mapObjects/CGHeroInstance.h" + +VCMI_LIB_NAMESPACE_BEGIN + +namespace Rewardable { + +struct Limiter; +using LimitersList = std::vector>; + +/// Limiters of rewards. Rewards will be granted to hero only if he satisfies requirements +/// Note: for this is only a test - it won't remove anything from hero (e.g. artifacts or creatures) +/// NOTE: in future should (partially) replace seer hut/quest guard quests checks +struct DLL_LINKAGE Limiter +{ + /// day of week, unused if 0, 1-7 will test for current day of week + si32 dayOfWeek; + si32 daysPassed; + + /// total experience that hero needs to have + si32 heroExperience; + + /// level that hero needs to have + si32 heroLevel; + + /// mana points that hero needs to have + si32 manaPoints; + + /// percentage of mana points that hero needs to have + si32 manaPercentage; + + /// resources player needs to have in order to trigger reward + TResources resources; + + /// skills hero needs to have + std::vector primary; + std::map secondary; + + /// artifacts that hero needs to have (equipped or in backpack) to trigger this + /// Note: does not checks for multiple copies of the same arts + std::vector artifacts; + + /// Spells that hero must have in the spellbook + std::vector spells; + + /// creatures that hero needs to have + std::vector creatures; + + /// sub-limiters, all must pass for this limiter to pass + LimitersList allOf; + + /// sub-limiters, at least one should pass for this limiter to pass + LimitersList anyOf; + + /// sub-limiters, none should pass for this limiter to pass + LimitersList noneOf; + + Limiter(): + dayOfWeek(0), + daysPassed(0), + heroExperience(0), + heroLevel(0), + manaPercentage(0), + manaPoints(0), + primary(GameConstants::PRIMARY_SKILLS, 0) + {} + + bool heroAllowed(const CGHeroInstance * hero) const; + + template void serialize(Handler &h, const int version) + { + h & dayOfWeek; + h & daysPassed; + h & heroExperience; + h & heroLevel; + h & manaPoints; + h & manaPercentage; + h & resources; + h & primary; + h & secondary; + h & artifacts; + h & creatures; + h & allOf; + h & anyOf; + h & noneOf; + } +}; + +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Reward.cpp b/lib/rewardable/Reward.cpp new file mode 100644 index 000000000..e3b42153d --- /dev/null +++ b/lib/rewardable/Reward.cpp @@ -0,0 +1,80 @@ +/* + * Reward.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 "Reward.h" + +VCMI_LIB_NAMESPACE_BEGIN + +si32 Rewardable::Reward::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 Rewardable::Reward::getDisplayedComponent(const CGHeroInstance * h) const +{ + std::vector comps; + loadComponents(comps, h); + assert(!comps.empty()); + return comps.front(); +} + +void Rewardable::Reward::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); + } +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/rewardable/Reward.h b/lib/rewardable/Reward.h new file mode 100644 index 000000000..b0923e022 --- /dev/null +++ b/lib/rewardable/Reward.h @@ -0,0 +1,118 @@ +/* + * Reward.h, 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 + * + */ + +#pragma once + +#include "../mapObjects/CGHeroInstance.h" + +VCMI_LIB_NAMESPACE_BEGIN + +namespace Rewardable +{ + +struct Reward; +using RewardsList = std::vector>; + +/// Reward that can be granted to a hero +/// NOTE: eventually should replace seer hut rewards and events/pandoras +struct DLL_LINKAGE Reward +{ + /// resources that will be given to player + TResources resources; + + /// received experience + si32 heroExperience; + /// received levels (converted into XP during grant) + si32 heroLevel; + + /// mana given to/taken from hero, fixed value + si32 manaDiff; + + /// if giving mana points puts hero above mana pool, any overflow will be multiplied by specified percentage + si32 manaOverflowFactor; + + /// fixed value, in form of percentage from max + si32 manaPercentage; + + /// movement points, only for current day. Bonuses should be used to grant MP on any other day + si32 movePoints; + /// fixed value, in form of percentage from max + si32 movePercentage; + + /// list of bonuses, e.g. morale/luck + std::vector bonuses; + + /// skills that hero may receive or lose + std::vector primary; + std::map secondary; + + /// creatures that will be changed in hero's army + std::map creaturesChange; + + /// objects that hero may receive + std::vector artifacts; + std::vector spells; + std::vector creatures; + + /// actions that hero may execute and object caster. Pair of spellID and school level + std::pair spellCast; + + /// list of components that will be added to reward description. First entry in list will override displayed component + std::vector extraComponents; + + /// if set to true, object will be removed after granting reward + bool removeObject; + + /// 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; + + Reward() : + heroExperience(0), + heroLevel(0), + manaDiff(0), + manaPercentage(-1), + movePoints(0), + movePercentage(-1), + primary(4, 0), + removeObject(false), + spellCast(SpellID::NONE, SecSkillLevel::NONE) + {} + + template void serialize(Handler &h, const int version) + { + h & resources; + h & extraComponents; + h & removeObject; + h & manaPercentage; + h & movePercentage; + h & heroExperience; + h & heroLevel; + h & manaDiff; + h & manaOverflowFactor; + h & movePoints; + h & primary; + h & secondary; + h & bonuses; + h & artifacts; + h & spells; + h & creatures; + h & creaturesChange; + if(version >= 821) + h & spellCast; + } +}; +} + +VCMI_LIB_NAMESPACE_END