1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Separate configuration and object

This commit is contained in:
nordsoft 2023-04-30 00:59:02 +04:00
parent bd1105e2b7
commit e47bb3f1f9
7 changed files with 274 additions and 252 deletions

View File

@ -319,7 +319,7 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj)
//TODO: allow polling of remaining creatures in dwelling
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
return rewardable->getResetDuration() == 7;
return rewardable->getConfiguration().getResetDuration() == 7;
if(dynamic_cast<const CGDwelling *>(obj))
return true;

View File

@ -72,10 +72,10 @@ void AIMemory::markObjectVisited(const CGObjectInstance * obj)
// TODO: maybe this logic belongs to CaptureObjects::shouldVisit
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(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;
}

View File

@ -1605,10 +1605,10 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(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<const CRewardableObject *>(obj))
return rewardable->getResetDuration() == 7;
return rewardable->getConfiguration().getResetDuration() == 7;
if(dynamic_cast<const CGDwelling *>(obj))
return true;

View File

@ -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<SpellID> 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<ui32>(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<si32, si32> & 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<si32, si32> 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_ptr<const ObjectTe
auto * ret = new CRewardableObject();
preInitObject(ret);
ret->appearance = tmpl;
ret->blockVisit = blockVisit;
return ret;
}
void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
{
objectInfo.configureObject(dynamic_cast<CRewardableObject*>(object), rng);
if(auto * rewardableObject = dynamic_cast<CRewardableObject*>(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<IObjectInfo> CRewardableConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const

View File

@ -20,13 +20,13 @@ class DLL_LINKAGE CRandomRewardObjectInfo : public IObjectInfo
{
JsonNode parameters;
void configureRewards(CRewardableObject * object, CRandomGenerator & rng, const JsonNode & source, std::map<si32, si32> & thrownDice, CRewardVisitInfo::ERewardEventType mode) const;
void configureRewards(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source, std::map<si32, si32> & 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;

View File

@ -114,7 +114,78 @@ bool CRewardLimiter::heroAllowed(const CGHeroInstance * hero) const
return false;
}
std::vector<ui32> 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<Component> comps;
loadComponents(comps, h);
assert(!comps.empty());
return comps.front();
}
void CRewardInfo::loadComponents(std::vector<Component> & comps,
const CGHeroInstance * h) const
{
for (auto comp : extraComponents)
comps.push_back(comp);
if (heroExperience)
{
comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast<si32>(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<primary.size(); i++)
{
if (primary[i] != 0)
comps.emplace_back(Component::EComponentType::PRIM_SKILL, static_cast<ui16>(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<resources.size(); i++)
{
if (resources[i] !=0)
comps.emplace_back(Component::EComponentType::RESOURCE, static_cast<ui16>(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<ui32> Rewardable::Configuration::getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event) const
{
std::vector<ui32> ret;
@ -131,20 +202,35 @@ std::vector<ui32> CRewardableObject::getAvailableRewards(const CGHeroInstance *
return ret;
}
Rewardable::EVisitMode Rewardable::Configuration::getVisitMode() const
{
return static_cast<EVisitMode>(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<ui32> & 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<EVisitMode>(visitMode);
}
ui16 CRewardableObject::getResetDuration() const
{
return resetParameters.period;
}
void CRewardInfo::loadComponents(std::vector<Component> & comps,
const CGHeroInstance * h) const
{
for (auto comp : extraComponents)
comps.push_back(comp);
if (heroExperience)
{
comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast<si32>(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<primary.size(); i++)
{
if (primary[i] != 0)
comps.emplace_back(Component::EComponentType::PRIM_SKILL, static_cast<ui16>(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<resources.size(); i++)
{
if (resources[i] !=0)
comps.emplace_back(Component::EComponentType::RESOURCE, static_cast<ui16>(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<Component> 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

View File

@ -183,6 +183,7 @@ public:
/// Generates list of components that describes reward for a specific hero
virtual void loadComponents(std::vector<Component> & 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<std::string, 3> SelectModeString{"selectFirst", "selectPlayer"};
const std::array<std::string, 5> 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<ui32> getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event ) const;
const std::array<std::string, 3> SelectModeString{"selectFirst", "selectPlayer"};
const std::array<std::string, 5> 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<CRewardVisitInfo> 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<CRewardVisitInfo> 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<ui32> getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event ) const;
void initObj(CRandomGenerator & rand);
template <typename Handler> 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 <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CArmedInstance&>(*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: