mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Rewardable objects may now define guards. Converted Crypt to rewardable.
This commit is contained in:
parent
a9c4683da6
commit
785036836c
@ -302,6 +302,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
|
||||
return iat.army.getStrength();
|
||||
}
|
||||
case Obj::MONSTER:
|
||||
case Obj::CRYPT:
|
||||
{
|
||||
//TODO!!!!!!!!
|
||||
const CGCreature * cre = dynamic_cast<const CGCreature *>(obj);
|
||||
@ -319,7 +320,6 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
|
||||
const CArmedInstance * a = dynamic_cast<const CArmedInstance *>(obj);
|
||||
return a->getArmyStrength();
|
||||
}
|
||||
case Obj::CRYPT: //crypt
|
||||
case Obj::CREATURE_BANK: //crebank
|
||||
case Obj::DRAGON_UTOPIA:
|
||||
case Obj::SHIPWRECK: //shipwreck
|
||||
|
@ -833,7 +833,7 @@
|
||||
},
|
||||
"crypt" : {
|
||||
"index" :84,
|
||||
"handler": "bank",
|
||||
"handler": "configurable",
|
||||
"base" : {
|
||||
"sounds" : {
|
||||
"ambient" : ["LOOPDEAD"],
|
||||
@ -843,16 +843,27 @@
|
||||
"types" : {
|
||||
"crypt" : {
|
||||
"index" : 0,
|
||||
"resetDuration" : 0,
|
||||
"name" : "Crypt",
|
||||
"aiValue" : 1500,
|
||||
|
||||
"rmg" : {
|
||||
"value" : 1000,
|
||||
"rarity" : 100
|
||||
},
|
||||
"levels": [
|
||||
|
||||
"visitMode" : "once",
|
||||
"selectMode" : "selectFirst",
|
||||
"onGuardedMessage" : 119, // Do you want to search the graves?
|
||||
"onVisited" : [
|
||||
{
|
||||
"chance": 30,
|
||||
"message" : 120, // Such a despicable act reduces your army's morale.
|
||||
"bonuses" : [ { "type" : "MORALE", "val" : -1, "duration" : "ONE_BATTLE" } ]
|
||||
}
|
||||
],
|
||||
|
||||
"rewards": [
|
||||
{
|
||||
"appearChance" : { "min" : 0, "max" : 30 },
|
||||
"guards": [
|
||||
{ "amount": 10, "type": "skeleton" },
|
||||
{ "amount": 10, "type": "walkingDead" },
|
||||
@ -860,17 +871,14 @@
|
||||
{ "amount": 10, "type": "skeleton" },
|
||||
{ "amount": 10, "type": "skeleton" }
|
||||
],
|
||||
"combat_value": 75,
|
||||
"reward" : {
|
||||
"value": 1500,
|
||||
"resources":
|
||||
{
|
||||
"gold" : 1500
|
||||
}
|
||||
"message" : 121, // you search the graves and find something
|
||||
"resources":
|
||||
{
|
||||
"gold" : 1500
|
||||
}
|
||||
},
|
||||
{
|
||||
"chance": 30,
|
||||
"appearChance" : { "min" : 30, "max" : 60 },
|
||||
"guards": [
|
||||
{ "amount": 13, "type": "skeleton" },
|
||||
{ "amount": 10, "type": "walkingDead" },
|
||||
@ -878,50 +886,42 @@
|
||||
{ "amount": 10, "type": "walkingDead" },
|
||||
{ "amount": 12, "type": "skeleton" }
|
||||
],
|
||||
"combat_value": 94,
|
||||
"reward" : {
|
||||
"value": 2000,
|
||||
"resources":
|
||||
{
|
||||
"gold" : 2000
|
||||
}
|
||||
"message" : 121, // you search the graves and find something
|
||||
"resources":
|
||||
{
|
||||
"gold" : 2000
|
||||
}
|
||||
},
|
||||
{
|
||||
"chance": 30,
|
||||
"appearChance" : { "min" : 60, "max" : 90 },
|
||||
"guards": [
|
||||
{ "amount": 20, "type": "skeleton" },
|
||||
{ "amount": 20, "type": "walkingDead" },
|
||||
{ "amount": 10, "type": "wight" },
|
||||
{ "amount": 5, "type": "vampire" }
|
||||
],
|
||||
"combat_value": 169,
|
||||
"reward" : {
|
||||
"value": 3500,
|
||||
"resources":
|
||||
{
|
||||
"gold" : 2500
|
||||
},
|
||||
"artifacts": [ { "class" : "TREASURE" } ]
|
||||
"message" : 121, // you search the graves and find something
|
||||
"resources":
|
||||
{
|
||||
"gold" : 2500
|
||||
},
|
||||
"artifacts": [ { "class" : "TREASURE" } ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"chance": 10,
|
||||
"appearChance" : { "min" : 90, "max" : 100 },
|
||||
"guards": [
|
||||
{ "amount": 20, "type": "skeleton" },
|
||||
{ "amount": 20, "type": "walkingDead" },
|
||||
{ "amount": 10, "type": "wight" },
|
||||
{ "amount": 10, "type": "vampire" }
|
||||
],
|
||||
"combat_value": 225,
|
||||
"reward" : {
|
||||
"value": 6000,
|
||||
"resources":
|
||||
{
|
||||
"gold" : 5000
|
||||
},
|
||||
"artifacts": [ { "class" : "TREASURE" } ]
|
||||
}
|
||||
"message" : 121, // you search the graves and find something
|
||||
"resources":
|
||||
{
|
||||
"gold" : 5000
|
||||
},
|
||||
"artifacts": [ { "class" : "TREASURE" } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -62,17 +62,20 @@ Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCal
|
||||
|
||||
void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RNG & rng) const
|
||||
{
|
||||
if(auto * rewardableObject = dynamic_cast<CRewardableObject*>(object))
|
||||
{
|
||||
rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID);
|
||||
auto * rewardableObject = dynamic_cast<CRewardableObject*>(object);
|
||||
|
||||
if (rewardableObject->configuration.info.empty())
|
||||
{
|
||||
if (objectInfo.getParameters()["rewards"].isNull())
|
||||
logMod->error("Object %s has invalid configuration! No defined rewards found!", getJsonKey());
|
||||
else
|
||||
logMod->error("Object %s has invalid configuration! Make sure that defined appear chances are continuous!", getJsonKey());
|
||||
}
|
||||
if (!rewardableObject)
|
||||
throw std::runtime_error("Object " + std::to_string(object->getObjGroupIndex()) + ", " + std::to_string(object->getObjTypeIndex()) + " is not a rewardable object!" );
|
||||
|
||||
rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID);
|
||||
rewardableObject->initializeGuards();
|
||||
|
||||
if (rewardableObject->configuration.info.empty())
|
||||
{
|
||||
if (objectInfo.getParameters()["rewards"].isNull())
|
||||
logMod->error("Object %s has invalid configuration! No defined rewards found!", getJsonKey());
|
||||
else
|
||||
logMod->error("Object %s has invalid configuration! Make sure that defined appear chances are continuous!", getJsonKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <vcmi/spells/Service.h>
|
||||
|
||||
#include "../texts/CGeneralTextHandler.h"
|
||||
#include "../CSoundBase.h"
|
||||
#include "../IGameSettings.h"
|
||||
#include "../CPlayerState.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@ -156,23 +155,18 @@ void CBank::onHeroVisit(const CGHeroInstance * h) const
|
||||
case Obj::DRAGON_UTOPIA:
|
||||
banktext = 47;
|
||||
break;
|
||||
case Obj::CRYPT:
|
||||
banktext = 119;
|
||||
break;
|
||||
case Obj::SHIPWRECK:
|
||||
banktext = 122;
|
||||
break;
|
||||
case Obj::PYRAMID:
|
||||
banktext = 105;
|
||||
break;
|
||||
case Obj::CREATURE_BANK:
|
||||
default:
|
||||
banktext = 32;
|
||||
break;
|
||||
}
|
||||
BlockingDialog bd(true, false);
|
||||
bd.player = h->getOwner();
|
||||
bd.soundID = soundBase::invalid; // Sound is handled in json files, else two sounds are played
|
||||
bd.text.appendLocalString(EMetaText::ADVOB_TXT, banktext);
|
||||
bd.components = getPopupComponents(h->getOwner());
|
||||
if (banktext == 32)
|
||||
@ -196,17 +190,12 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
case Obj::DERELICT_SHIP:
|
||||
textID = 43;
|
||||
break;
|
||||
case Obj::CRYPT:
|
||||
textID = 121;
|
||||
break;
|
||||
case Obj::SHIPWRECK:
|
||||
textID = 124;
|
||||
break;
|
||||
case Obj::PYRAMID:
|
||||
textID = 106;
|
||||
break;
|
||||
case Obj::CREATURE_BANK:
|
||||
case Obj::DRAGON_UTOPIA:
|
||||
default:
|
||||
textID = 34;
|
||||
break;
|
||||
@ -218,7 +207,6 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
{
|
||||
case Obj::SHIPWRECK:
|
||||
case Obj::DERELICT_SHIP:
|
||||
case Obj::CRYPT:
|
||||
{
|
||||
GiveBonus gbonus;
|
||||
gbonus.id = hero->id;
|
||||
@ -237,14 +225,9 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
textID = 42;
|
||||
gbonus.bonus.description = MetaString::createFromTextID("core.arraytxt.101");
|
||||
break;
|
||||
case Obj::CRYPT:
|
||||
textID = 120;
|
||||
gbonus.bonus.description = MetaString::createFromTextID("core.arraytxt.98");
|
||||
break;
|
||||
}
|
||||
cb->giveHeroBonus(&gbonus);
|
||||
iw.components.emplace_back(ComponentType::MORALE, -1);
|
||||
iw.soundID = soundBase::invalid;
|
||||
break;
|
||||
}
|
||||
case Obj::PYRAMID:
|
||||
@ -258,8 +241,6 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
iw.components.emplace_back(ComponentType::LUCK, -2);
|
||||
break;
|
||||
}
|
||||
case Obj::CREATURE_BANK:
|
||||
case Obj::DRAGON_UTOPIA:
|
||||
default:
|
||||
iw.text.appendRawString(VLC->generaltexth->advobtxt[33]);// This was X, now is completely empty
|
||||
iw.text.replaceRawString(getObjectName());
|
||||
|
@ -10,16 +10,19 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "CRewardableObject.h"
|
||||
#include "../gameState/CGameState.h"
|
||||
#include "../texts/CGeneralTextHandler.h"
|
||||
|
||||
#include "../CPlayerState.h"
|
||||
#include "../GameSettings.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../gameState/CGameState.h"
|
||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../mapObjectConstructors/CRewardableConstructor.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../networkPacks/PacksForClient.h"
|
||||
#include "../networkPacks/PacksForClientBattle.h"
|
||||
#include "../serializer/JsonSerializeFormat.h"
|
||||
#include "../texts/CGeneralTextHandler.h"
|
||||
|
||||
#include <vstd/RNG.h>
|
||||
|
||||
@ -91,7 +94,42 @@ std::vector<Component> CRewardableObject::loadComponents(const CGHeroInstance *
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CRewardableObject::guardedPotentially() const
|
||||
{
|
||||
for (auto const & visitInfo : configuration.info)
|
||||
if (!visitInfo.reward.guards.empty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CRewardableObject::guardedPresently() const
|
||||
{
|
||||
return stacksCount() > 0;
|
||||
}
|
||||
|
||||
void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const
|
||||
{
|
||||
if (guardedPresently())
|
||||
{
|
||||
auto guardedIndexes = getAvailableRewards(h, Rewardable::EEventType::EVENT_GUARDED);
|
||||
auto guardedReward = configuration.info.at(guardedIndexes.at(0));
|
||||
|
||||
// ask player to confirm attack
|
||||
BlockingDialog bd(true, false);
|
||||
bd.player = h->getOwner();
|
||||
bd.text = guardedReward.message;
|
||||
bd.components = getPopupComponents(h->getOwner());
|
||||
|
||||
cb->showBlockingDialog(&bd);
|
||||
}
|
||||
else
|
||||
{
|
||||
doHeroVisit(h);
|
||||
}
|
||||
}
|
||||
|
||||
void CRewardableObject::doHeroVisit(const CGHeroInstance *h) const
|
||||
{
|
||||
if(!wasVisitedBefore(h))
|
||||
{
|
||||
@ -181,39 +219,54 @@ void CRewardableObject::heroLevelUpDone(const CGHeroInstance *hero) const
|
||||
grantRewardAfterLevelup(cb, configuration.info.at(selectedReward), this, hero);
|
||||
}
|
||||
|
||||
void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const
|
||||
void CRewardableObject::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
||||
{
|
||||
if(answer == 0)
|
||||
if (result.winner == BattleSide::ATTACKER)
|
||||
{
|
||||
switch (configuration.visitMode)
|
||||
{
|
||||
case Rewardable::VISIT_UNLIMITED:
|
||||
case Rewardable::VISIT_BONUS:
|
||||
case Rewardable::VISIT_HERO:
|
||||
case Rewardable::VISIT_LIMITER:
|
||||
{
|
||||
// workaround for object with refusable reward not getting marked as visited
|
||||
// TODO: better solution that would also work for player-visitable objects
|
||||
if (!wasScouted(hero->getOwner()))
|
||||
{
|
||||
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, hero->id);
|
||||
cb->sendAndApply(&cov);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return; // player refused
|
||||
doHeroVisit(hero);
|
||||
}
|
||||
}
|
||||
|
||||
if(answer > 0 && answer-1 < configuration.info.size())
|
||||
void CRewardableObject::blockingDialogAnswered(const CGHeroInstance * hero, int32_t answer) const
|
||||
{
|
||||
if(guardedPresently())
|
||||
{
|
||||
auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
||||
markAsVisited(hero);
|
||||
grantReward(list[answer - 1], hero);
|
||||
if (answer)
|
||||
cb->startBattleI(hero, this, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unhandled choice");
|
||||
if(answer == 0)
|
||||
{
|
||||
switch(configuration.visitMode)
|
||||
{
|
||||
case Rewardable::VISIT_UNLIMITED:
|
||||
case Rewardable::VISIT_BONUS:
|
||||
case Rewardable::VISIT_HERO:
|
||||
case Rewardable::VISIT_LIMITER:
|
||||
{
|
||||
// workaround for object with refusable reward not getting marked as visited
|
||||
// TODO: better solution that would also work for player-visitable objects
|
||||
if(!wasScouted(hero->getOwner()))
|
||||
{
|
||||
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, hero->id);
|
||||
cb->sendAndApply(&cov);
|
||||
}
|
||||
}
|
||||
}
|
||||
return; // player refused
|
||||
}
|
||||
|
||||
if(answer > 0 && answer - 1 < configuration.info.size())
|
||||
{
|
||||
auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
||||
markAsVisited(hero);
|
||||
grantReward(list[answer - 1], hero);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unhandled choice");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,19 +421,41 @@ std::vector<Component> CRewardableObject::getPopupComponentsImpl(PlayerColor pla
|
||||
if (!configuration.showScoutedPreview)
|
||||
return {};
|
||||
|
||||
auto rewardIndices = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
||||
if (rewardIndices.empty() && !configuration.info.empty())
|
||||
if (guardedPresently())
|
||||
{
|
||||
// Object has valid config, but current hero has no rewards that he can receive.
|
||||
// Usually this happens if hero has already visited this object -> show reward using context without any hero
|
||||
// since reward may be context-sensitive - e.g. Witch Hut that gives 1 skill, but always at basic level
|
||||
return loadComponents(nullptr, {0});
|
||||
if (!VLC->settings()->getBoolean(EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION))
|
||||
return {};
|
||||
|
||||
std::map<CreatureID, int> guardsAmounts;
|
||||
std::vector<Component> result;
|
||||
|
||||
for (auto const & slot : Slots())
|
||||
if (slot.second)
|
||||
guardsAmounts[slot.second->getCreatureID()] += slot.second->getCount();
|
||||
|
||||
for (auto const & guard : guardsAmounts)
|
||||
{
|
||||
Component comp(ComponentType::CREATURE, guard.first, guard.second);
|
||||
result.push_back(comp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rewardIndices = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT);
|
||||
if (rewardIndices.empty() && !configuration.info.empty())
|
||||
{
|
||||
// Object has valid config, but current hero has no rewards that he can receive.
|
||||
// Usually this happens if hero has already visited this object -> show reward using context without any hero
|
||||
// since reward may be context-sensitive - e.g. Witch Hut that gives 1 skill, but always at basic level
|
||||
return loadComponents(nullptr, {0});
|
||||
}
|
||||
|
||||
if (rewardIndices.empty())
|
||||
return {};
|
||||
if (rewardIndices.empty())
|
||||
return {};
|
||||
|
||||
return loadComponents(hero, rewardIndices);
|
||||
return loadComponents(hero, rewardIndices);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Component> CRewardableObject::getPopupComponents(PlayerColor player) const
|
||||
@ -440,4 +515,21 @@ void CRewardableObject::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
handler.serializeStruct("rewardable", static_cast<Rewardable::Interface&>(*this));
|
||||
}
|
||||
|
||||
void CRewardableObject::initializeGuards()
|
||||
{
|
||||
clearSlots();
|
||||
|
||||
for (auto const & visitInfo : configuration.info)
|
||||
{
|
||||
for (auto const & guard : visitInfo.reward.guards)
|
||||
{
|
||||
auto slotID = getFreeSlot();
|
||||
if (!slotID.validSlot())
|
||||
return;
|
||||
|
||||
putStack(slotID, new CStackInstance(guard.getId(), guard.getCount()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -45,7 +45,14 @@ protected:
|
||||
std::string getDescriptionMessage(PlayerColor player, const CGHeroInstance * hero) const;
|
||||
std::vector<Component> getPopupComponentsImpl(PlayerColor player, const CGHeroInstance * hero) const;
|
||||
|
||||
void doHeroVisit(const CGHeroInstance *h) const;
|
||||
|
||||
/// Returns true if this object might have guards present, whether they were cleared or not
|
||||
bool guardedPotentially() const;
|
||||
/// Returns true if this object is currently guarded
|
||||
bool guardedPresently() const;
|
||||
public:
|
||||
|
||||
/// 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;
|
||||
@ -56,6 +63,8 @@ public:
|
||||
/// gives reward to player or ask for choice in case of multiple rewards
|
||||
void onHeroVisit(const CGHeroInstance *h) const override;
|
||||
|
||||
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||
|
||||
///possibly resets object state
|
||||
void newTurn(vstd::RNG & rand) const override;
|
||||
|
||||
@ -66,6 +75,8 @@ public:
|
||||
void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override;
|
||||
|
||||
void initObj(vstd::RNG & rand) override;
|
||||
|
||||
void initializeGuards();
|
||||
|
||||
void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
|
||||
|
||||
|
0
lib/mapObjects/CreatureBank.cpp
Normal file
0
lib/mapObjects/CreatureBank.cpp
Normal file
0
lib/mapObjects/CreatureBank.h
Normal file
0
lib/mapObjects/CreatureBank.h
Normal file
@ -2434,6 +2434,7 @@ void SetRewardableConfiguration::applyGs(CGameState *gs)
|
||||
auto * rewardablePtr = dynamic_cast<CRewardableObject *>(objectPtr);
|
||||
assert(rewardablePtr);
|
||||
rewardablePtr->configuration = configuration;
|
||||
rewardablePtr->initializeGuards();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -44,7 +44,8 @@ enum class EEventType
|
||||
EVENT_INVALID = 0,
|
||||
EVENT_FIRST_VISIT,
|
||||
EVENT_ALREADY_VISITED,
|
||||
EVENT_NOT_AVAILABLE
|
||||
EVENT_NOT_AVAILABLE,
|
||||
EVENT_GUARDED
|
||||
};
|
||||
|
||||
constexpr std::array<std::string_view, 4> SelectModeString{"selectFirst", "selectPlayer", "selectRandom", "selectAll"};
|
||||
|
@ -174,6 +174,8 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, vstd:
|
||||
reward.removeObject = source["removeObject"].Bool();
|
||||
reward.bonuses = randomizer.loadBonuses(source["bonuses"]);
|
||||
|
||||
reward.guards = randomizer.loadCreatures(source["guards"], rng, variables);
|
||||
|
||||
reward.primary = randomizer.loadPrimaries(source["primary"], rng, variables);
|
||||
reward.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
|
||||
|
||||
@ -378,6 +380,16 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd:
|
||||
object.info.push_back(onEmpty);
|
||||
}
|
||||
|
||||
if (!parameters["onGuardedMessage"].isNull())
|
||||
{
|
||||
Rewardable::VisitInfo onGuarded;
|
||||
onGuarded.visitType = Rewardable::EEventType::EVENT_GUARDED;
|
||||
onGuarded.message = loadMessage(parameters["onGuardedMessage"], TextIdentifier(objectTextID, "onGuarded"));
|
||||
replaceTextPlaceholders(onGuarded.message, object.variables);
|
||||
|
||||
object.info.push_back(onGuarded);
|
||||
}
|
||||
|
||||
configureResetInfo(object, rng, object.resetParameters, parameters["resetParameters"]);
|
||||
|
||||
object.canRefuse = parameters["canRefuse"].Bool();
|
||||
|
@ -82,6 +82,9 @@ struct DLL_LINKAGE Reward final
|
||||
/// fixed value, in form of percentage from max
|
||||
si32 movePercentage;
|
||||
|
||||
/// Guards that must be defeated in order to access this reward, empty if not guarded
|
||||
std::vector<CStackBasicDescriptor> guards;
|
||||
|
||||
/// list of bonuses, e.g. morale/luck
|
||||
std::vector<Bonus> bonuses;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user