mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-15 00:05:02 +02:00
Separate configuration and object
This commit is contained in:
@ -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
|
||||
|
Reference in New Issue
Block a user