1
0
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:
nordsoft
2023-04-30 00:59:02 +04:00
parent bd1105e2b7
commit e47bb3f1f9
7 changed files with 274 additions and 252 deletions

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