mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-10 22:31:40 +02:00
Add new rewards for configurable objects
This commit is contained in:
@@ -265,9 +265,9 @@ uint64_t RewardEvaluator::getArmyReward(
|
|||||||
|
|
||||||
auto rewardValue = 0;
|
auto rewardValue = 0;
|
||||||
|
|
||||||
if(!info.reward.artifacts.empty())
|
if(!info.reward.grantedArtifacts.empty())
|
||||||
{
|
{
|
||||||
for(auto artID : info.reward.artifacts)
|
for(auto artID : info.reward.grantedArtifacts)
|
||||||
{
|
{
|
||||||
const auto * art = artID.toArtifact();
|
const auto * art = artID.toArtifact();
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ uint64_t RewardEvaluator::getArmyReward(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
totalValue += rewardValue > 0 ? rewardValue / (info.reward.artifacts.size() + info.reward.creatures.size()) : 0;
|
totalValue += rewardValue > 0 ? rewardValue / (info.reward.grantedArtifacts.size() + info.reward.creatures.size()) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalValue;
|
return totalValue;
|
||||||
|
@@ -47,6 +47,15 @@ std::shared_ptr<CGObjectInstance> CRewardableConstructor::create(IGameCallback *
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRewardableConstructor::assignBonuses(std::vector<Bonus> & bonuses, MapObjectID objectID) const
|
||||||
|
{
|
||||||
|
for (auto & bonus : bonuses)
|
||||||
|
{
|
||||||
|
bonus.source = BonusSource::OBJECT_TYPE;
|
||||||
|
bonus.sid = BonusSourceID(objectID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const
|
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const
|
||||||
{
|
{
|
||||||
Rewardable::Configuration result;
|
Rewardable::Configuration result;
|
||||||
@@ -62,11 +71,8 @@ Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCal
|
|||||||
|
|
||||||
for(auto & rewardInfo : result.info)
|
for(auto & rewardInfo : result.info)
|
||||||
{
|
{
|
||||||
for (auto & bonus : rewardInfo.reward.bonuses)
|
assignBonuses(rewardInfo.reward.heroBonuses, objectID);
|
||||||
{
|
assignBonuses(rewardInfo.reward.playerBonuses, objectID);
|
||||||
bonus.source = BonusSource::OBJECT_TYPE;
|
|
||||||
bonus.sid = BonusSourceID(objectID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@@ -14,10 +14,13 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct Bonus;
|
||||||
|
|
||||||
class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler
|
class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler
|
||||||
{
|
{
|
||||||
Rewardable::Info objectInfo;
|
Rewardable::Info objectInfo;
|
||||||
|
|
||||||
|
void assignBonuses(std::vector<Bonus> & bonuses, MapObjectID objectID) const;
|
||||||
void initTypeData(const JsonNode & config) override;
|
void initTypeData(const JsonNode & config) override;
|
||||||
|
|
||||||
bool blockVisit = false;
|
bool blockVisit = false;
|
||||||
|
@@ -83,7 +83,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
|
|||||||
temp.heroLevel = vi.reward.heroLevel;
|
temp.heroLevel = vi.reward.heroLevel;
|
||||||
temp.primary = vi.reward.primary;
|
temp.primary = vi.reward.primary;
|
||||||
temp.secondary = vi.reward.secondary;
|
temp.secondary = vi.reward.secondary;
|
||||||
temp.bonuses = vi.reward.bonuses;
|
temp.heroBonuses = vi.reward.heroBonuses;
|
||||||
temp.manaDiff = vi.reward.manaDiff;
|
temp.manaDiff = vi.reward.manaDiff;
|
||||||
temp.manaPercentage = vi.reward.manaPercentage;
|
temp.manaPercentage = vi.reward.manaPercentage;
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
|
|||||||
if(vi.reward.manaDiff || vi.reward.manaPercentage >= 0)
|
if(vi.reward.manaDiff || vi.reward.manaPercentage >= 0)
|
||||||
txt = setText(temp.manaDiff > 0, 177, 176, h);
|
txt = setText(temp.manaDiff > 0, 177, 176, h);
|
||||||
|
|
||||||
for(auto b : vi.reward.bonuses)
|
for(auto b : vi.reward.heroBonuses)
|
||||||
{
|
{
|
||||||
if(b.val && b.type == BonusType::MORALE)
|
if(b.val && b.type == BonusType::MORALE)
|
||||||
txt = setText(b.val > 0, 179, 178, h);
|
txt = setText(b.val > 0, 179, 178, h);
|
||||||
@@ -122,7 +122,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
|
|||||||
|
|
||||||
//artifacts message
|
//artifacts message
|
||||||
temp = Rewardable::Reward{};
|
temp = Rewardable::Reward{};
|
||||||
temp.artifacts = vi.reward.artifacts;
|
temp.grantedArtifacts = vi.reward.grantedArtifacts;
|
||||||
sendInfoWindow(setText(true, 183, 183, h), temp);
|
sendInfoWindow(setText(true, 183, 183, h), temp);
|
||||||
|
|
||||||
//creatures message
|
//creatures message
|
||||||
@@ -160,8 +160,8 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
|
|||||||
temp.manaPercentage = -1;
|
temp.manaPercentage = -1;
|
||||||
temp.spells.clear();
|
temp.spells.clear();
|
||||||
temp.creatures.clear();
|
temp.creatures.clear();
|
||||||
temp.bonuses.clear();
|
temp.heroBonuses.clear();
|
||||||
temp.artifacts.clear();
|
temp.grantedArtifacts.clear();
|
||||||
sendInfoWindow(setText(true, 175, 175, h), temp);
|
sendInfoWindow(setText(true, 175, 175, h), temp);
|
||||||
|
|
||||||
// grant reward afterwards. Note that it may remove object
|
// grant reward afterwards. Note that it may remove object
|
||||||
@@ -229,11 +229,11 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
int val = 0;
|
int val = 0;
|
||||||
handler.serializeInt("morale", val, 0);
|
handler.serializeInt("morale", val, 0);
|
||||||
if(val)
|
if(val)
|
||||||
vinfo.reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
vinfo.reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
||||||
|
|
||||||
handler.serializeInt("luck", val, 0);
|
handler.serializeInt("luck", val, 0);
|
||||||
if(val)
|
if(val)
|
||||||
vinfo.reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
vinfo.reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
||||||
|
|
||||||
vinfo.reward.resources.serializeJson(handler, "resources");
|
vinfo.reward.resources.serializeJson(handler, "resources");
|
||||||
{
|
{
|
||||||
@@ -246,7 +246,7 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.serializeIdArray("artifacts", vinfo.reward.artifacts);
|
handler.serializeIdArray("artifacts", vinfo.reward.grantedArtifacts);
|
||||||
handler.serializeIdArray("spells", vinfo.reward.spells);
|
handler.serializeIdArray("spells", vinfo.reward.spells);
|
||||||
handler.enterArray("creatures").serializeStruct(vinfo.reward.creatures);
|
handler.enterArray("creatures").serializeStruct(vinfo.reward.creatures);
|
||||||
|
|
||||||
@@ -279,8 +279,8 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
|| vinfo.reward.heroExperience
|
|| vinfo.reward.heroExperience
|
||||||
|| vinfo.reward.manaDiff
|
|| vinfo.reward.manaDiff
|
||||||
|| vinfo.reward.resources.nonZero()
|
|| vinfo.reward.resources.nonZero()
|
||||||
|| !vinfo.reward.artifacts.empty()
|
|| !vinfo.reward.grantedArtifacts.empty()
|
||||||
|| !vinfo.reward.bonuses.empty()
|
|| !vinfo.reward.heroBonuses.empty()
|
||||||
|| !vinfo.reward.creatures.empty()
|
|| !vinfo.reward.creatures.empty()
|
||||||
|| !vinfo.reward.secondary.empty();
|
|| !vinfo.reward.secondary.empty();
|
||||||
|
|
||||||
|
@@ -714,9 +714,9 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
if(metaTypeName == "mana")
|
if(metaTypeName == "mana")
|
||||||
reward.manaDiff = val;
|
reward.manaDiff = val;
|
||||||
if(metaTypeName == "morale")
|
if(metaTypeName == "morale")
|
||||||
reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
||||||
if(metaTypeName == "luck")
|
if(metaTypeName == "luck")
|
||||||
reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(id));
|
||||||
if(metaTypeName == "resource")
|
if(metaTypeName == "resource")
|
||||||
{
|
{
|
||||||
auto rawId = *LIBRARY->identifiers()->getIdentifier(ModScope::scopeMap(), fullIdentifier, false);
|
auto rawId = *LIBRARY->identifiers()->getIdentifier(ModScope::scopeMap(), fullIdentifier, false);
|
||||||
@@ -735,7 +735,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
if(metaTypeName == "artifact")
|
if(metaTypeName == "artifact")
|
||||||
{
|
{
|
||||||
auto rawId = *LIBRARY->identifiers()->getIdentifier(ModScope::scopeMap(), fullIdentifier, false);
|
auto rawId = *LIBRARY->identifiers()->getIdentifier(ModScope::scopeMap(), fullIdentifier, false);
|
||||||
reward.artifacts.push_back(rawId);
|
reward.grantedArtifacts.push_back(rawId);
|
||||||
}
|
}
|
||||||
if(metaTypeName == "spell")
|
if(metaTypeName == "spell")
|
||||||
{
|
{
|
||||||
|
@@ -72,6 +72,25 @@ TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance *
|
|||||||
configuration = generateConfiguration(rand);
|
configuration = generateConfiguration(rand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TownRewardableBuildingInstance::assignBonuses(std::vector<Bonus> & bonuses) const
|
||||||
|
{
|
||||||
|
const auto & building = town->getTown()->buildings.at(getBuildingType());
|
||||||
|
|
||||||
|
for (auto & bonus : bonuses)
|
||||||
|
{
|
||||||
|
if (building->mapObjectLikeBonuses.hasValue())
|
||||||
|
{
|
||||||
|
bonus.source = BonusSource::OBJECT_TYPE;
|
||||||
|
bonus.sid = BonusSourceID(building->mapObjectLikeBonuses);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bonus.source = BonusSource::TOWN_STRUCTURE;
|
||||||
|
bonus.sid = BonusSourceID(building->getUniqueTypeID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(vstd::RNG & rand) const
|
Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(vstd::RNG & rand) const
|
||||||
{
|
{
|
||||||
Rewardable::Configuration result;
|
Rewardable::Configuration result;
|
||||||
@@ -82,19 +101,8 @@ Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(
|
|||||||
building->rewardableObjectInfo.configureObject(result, rand, cb);
|
building->rewardableObjectInfo.configureObject(result, rand, cb);
|
||||||
for(auto & rewardInfo : result.info)
|
for(auto & rewardInfo : result.info)
|
||||||
{
|
{
|
||||||
for (auto & bonus : rewardInfo.reward.bonuses)
|
assignBonuses(rewardInfo.reward.heroBonuses);
|
||||||
{
|
assignBonuses(rewardInfo.reward.playerBonuses);
|
||||||
if (building->mapObjectLikeBonuses.hasValue())
|
|
||||||
{
|
|
||||||
bonus.source = BonusSource::OBJECT_TYPE;
|
|
||||||
bonus.sid = BonusSourceID(building->mapObjectLikeBonuses);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bonus.source = BonusSource::TOWN_STRUCTURE;
|
|
||||||
bonus.sid = BonusSourceID(building->getUniqueTypeID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@@ -58,6 +58,7 @@ class DLL_LINKAGE TownRewardableBuildingInstance : public TownBuildingInstance,
|
|||||||
bool wasVisitedBefore(const CGHeroInstance * contextHero) const override;
|
bool wasVisitedBefore(const CGHeroInstance * contextHero) const override;
|
||||||
void grantReward(ui32 rewardID, const CGHeroInstance * hero) const override;
|
void grantReward(ui32 rewardID, const CGHeroInstance * hero) const override;
|
||||||
Rewardable::Configuration generateConfiguration(vstd::RNG & rand) const;
|
Rewardable::Configuration generateConfiguration(vstd::RNG & rand) const;
|
||||||
|
void assignBonuses(std::vector<Bonus> & bonuses) const;
|
||||||
|
|
||||||
const IObjectInterface * getObject() const override;
|
const IObjectInterface * getObject() const override;
|
||||||
bool wasVisited(PlayerColor player) const override;
|
bool wasVisited(PlayerColor player) const override;
|
||||||
|
@@ -1111,9 +1111,9 @@ void CMapLoaderH3M::readBoxContent(CGPandoraBox * object, const int3 & mapPositi
|
|||||||
reward.heroExperience = reader->readUInt32();
|
reward.heroExperience = reader->readUInt32();
|
||||||
reward.manaDiff = reader->readInt32();
|
reward.manaDiff = reader->readInt32();
|
||||||
if(auto val = reader->readInt8Checked(-3, 3))
|
if(auto val = reader->readInt8Checked(-3, 3))
|
||||||
reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(idToBeGiven));
|
reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(idToBeGiven));
|
||||||
if(auto val = reader->readInt8Checked(-3, 3))
|
if(auto val = reader->readInt8Checked(-3, 3))
|
||||||
reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(idToBeGiven));
|
reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(idToBeGiven));
|
||||||
|
|
||||||
reader->readResources(reward.resources);
|
reader->readResources(reward.resources);
|
||||||
for(int x = 0; x < GameConstants::PRIMARY_SKILLS; ++x)
|
for(int x = 0; x < GameConstants::PRIMARY_SKILLS; ++x)
|
||||||
@@ -1130,11 +1130,11 @@ void CMapLoaderH3M::readBoxContent(CGPandoraBox * object, const int3 & mapPositi
|
|||||||
size_t gart = reader->readUInt8(); //number of gained artifacts
|
size_t gart = reader->readUInt8(); //number of gained artifacts
|
||||||
for(size_t oo = 0; oo < gart; ++oo)
|
for(size_t oo = 0; oo < gart; ++oo)
|
||||||
{
|
{
|
||||||
reward.artifacts.push_back(reader->readArtifact());
|
reward.grantedArtifacts.push_back(reader->readArtifact());
|
||||||
if (features.levelHOTA5)
|
if (features.levelHOTA5)
|
||||||
{
|
{
|
||||||
SpellID scrollSpell = reader->readSpell16();
|
SpellID scrollSpell = reader->readSpell16();
|
||||||
if (reward.artifacts.back() == ArtifactID::SPELL_SCROLL)
|
if (reward.grantedArtifacts.back() == ArtifactID::SPELL_SCROLL)
|
||||||
logGlobal->warn("Map '%s': Pandora/Event at %s Option to give spell scroll (%s) via event or pandora is not implemented!", mapName, mapPosition.toString(), scrollSpell.toEntity(LIBRARY)->getJsonKey());
|
logGlobal->warn("Map '%s': Pandora/Event at %s Option to give spell scroll (%s) via event or pandora is not implemented!", mapName, mapPosition.toString(), scrollSpell.toEntity(LIBRARY)->getJsonKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2300,12 +2300,12 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, con
|
|||||||
}
|
}
|
||||||
case ESeerHutRewardType::MORALE:
|
case ESeerHutRewardType::MORALE:
|
||||||
{
|
{
|
||||||
reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, reader->readInt8Checked(-3, 3), BonusSourceID(idToBeGiven));
|
reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT_INSTANCE, reader->readInt8Checked(-3, 3), BonusSourceID(idToBeGiven));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESeerHutRewardType::LUCK:
|
case ESeerHutRewardType::LUCK:
|
||||||
{
|
{
|
||||||
reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, reader->readInt8Checked(-3, 3), BonusSourceID(idToBeGiven));
|
reward.heroBonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT_INSTANCE, reader->readInt8Checked(-3, 3), BonusSourceID(idToBeGiven));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESeerHutRewardType::RESOURCES:
|
case ESeerHutRewardType::RESOURCES:
|
||||||
@@ -2334,11 +2334,11 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, con
|
|||||||
}
|
}
|
||||||
case ESeerHutRewardType::ARTIFACT:
|
case ESeerHutRewardType::ARTIFACT:
|
||||||
{
|
{
|
||||||
reward.artifacts.push_back(reader->readArtifact());
|
reward.grantedArtifacts.push_back(reader->readArtifact());
|
||||||
if (features.levelHOTA5)
|
if (features.levelHOTA5)
|
||||||
{
|
{
|
||||||
SpellID scrollSpell = reader->readSpell16();
|
SpellID scrollSpell = reader->readSpell16();
|
||||||
if (reward.artifacts.back() == ArtifactID::SPELL_SCROLL)
|
if (reward.grantedArtifacts.back() == ArtifactID::SPELL_SCROLL)
|
||||||
logGlobal->warn("Map '%s': Seer Hut at %s: Option to give spell scroll (%s) as a reward is not implemented!", mapName, position.toString(), scrollSpell.toEntity(LIBRARY)->getJsonKey());
|
logGlobal->warn("Map '%s': Seer Hut at %s: Option to give spell scroll (%s) as a reward is not implemented!", mapName, position.toString(), scrollSpell.toEntity(LIBRARY)->getJsonKey());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -408,6 +408,7 @@ struct DLL_LINKAGE SetAvailableHero : public CPackForClient
|
|||||||
|
|
||||||
struct DLL_LINKAGE GiveBonus : public CPackForClient
|
struct DLL_LINKAGE GiveBonus : public CPackForClient
|
||||||
{
|
{
|
||||||
|
using VariantType = VariantIdentifier<ObjectInstanceID, PlayerColor, BattleID>;
|
||||||
enum class ETarget : int8_t { OBJECT, PLAYER, BATTLE };
|
enum class ETarget : int8_t { OBJECT, PLAYER, BATTLE };
|
||||||
|
|
||||||
explicit GiveBonus(ETarget Who = ETarget::OBJECT)
|
explicit GiveBonus(ETarget Who = ETarget::OBJECT)
|
||||||
@@ -415,10 +416,17 @@ struct DLL_LINKAGE GiveBonus : public CPackForClient
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GiveBonus(ETarget who, const VariantType & id, const Bonus & bonus)
|
||||||
|
: who(who)
|
||||||
|
, id(id)
|
||||||
|
, bonus(bonus)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void applyGs(CGameState * gs) override;
|
void applyGs(CGameState * gs) override;
|
||||||
|
|
||||||
ETarget who = ETarget::OBJECT;
|
ETarget who = ETarget::OBJECT;
|
||||||
VariantIdentifier<ObjectInstanceID, PlayerColor, BattleID> id;
|
VariantType id;
|
||||||
Bonus bonus;
|
Bonus bonus;
|
||||||
|
|
||||||
void visitTyped(ICPackVisitor & visitor) override;
|
void visitTyped(ICPackVisitor & visitor) override;
|
||||||
|
@@ -29,7 +29,8 @@ enum EVisitMode
|
|||||||
VISIT_HERO, // every hero can visit object once
|
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_BONUS, // can be visited by any hero that don't have bonus from this object
|
||||||
VISIT_LIMITER, // can be visited by heroes that don't fulfill provided limiter
|
VISIT_LIMITER, // can be visited by heroes that don't fulfill provided limiter
|
||||||
VISIT_PLAYER // every player can visit object once
|
VISIT_PLAYER, // every player can visit object once
|
||||||
|
VISIT_PLAYER_GLOBAL // every player can visit object once. All objects of the same type will be considered as visited
|
||||||
};
|
};
|
||||||
|
|
||||||
/// controls selection of reward granted to player
|
/// controls selection of reward granted to player
|
||||||
@@ -70,6 +71,9 @@ struct DLL_LINKAGE ResetInfo
|
|||||||
/// if true - re-randomize rewards on a new week
|
/// if true - re-randomize rewards on a new week
|
||||||
bool rewards;
|
bool rewards;
|
||||||
|
|
||||||
|
/// Reset object after visit by a hero, whether hero accepted reward or not
|
||||||
|
bool resetAfterVisit = false;
|
||||||
|
|
||||||
void serializeJson(JsonSerializeFormat & handler);
|
void serializeJson(JsonSerializeFormat & handler);
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
@@ -77,6 +81,8 @@ struct DLL_LINKAGE ResetInfo
|
|||||||
h & period;
|
h & period;
|
||||||
h & visitors;
|
h & visitors;
|
||||||
h & rewards;
|
h & rewards;
|
||||||
|
if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS)
|
||||||
|
h & resetAfterVisit;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -174,14 +174,15 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, vstd:
|
|||||||
reward.movePercentage = randomizer.loadValue(source["movePercentage"], rng, variables, -1);
|
reward.movePercentage = randomizer.loadValue(source["movePercentage"], rng, variables, -1);
|
||||||
|
|
||||||
reward.removeObject = source["removeObject"].Bool();
|
reward.removeObject = source["removeObject"].Bool();
|
||||||
reward.bonuses = randomizer.loadBonuses(source["bonuses"]);
|
reward.heroBonuses = randomizer.loadBonuses(source["bonuses"]);
|
||||||
|
reward.playerBonuses = randomizer.loadBonuses(source["playerBonuses"]);
|
||||||
|
|
||||||
reward.guards = randomizer.loadCreatures(source["guards"], rng, variables);
|
reward.guards = randomizer.loadCreatures(source["guards"], rng, variables);
|
||||||
|
|
||||||
reward.primary = randomizer.loadPrimaries(source["primary"], rng, variables);
|
reward.primary = randomizer.loadPrimaries(source["primary"], rng, variables);
|
||||||
reward.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
|
reward.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
|
||||||
|
|
||||||
reward.artifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
|
reward.grantedArtifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
|
||||||
reward.spells = randomizer.loadSpells(source["spells"], rng, variables);
|
reward.spells = randomizer.loadSpells(source["spells"], rng, variables);
|
||||||
reward.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
|
reward.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
|
||||||
if(!source["spellCast"].isNull() && source["spellCast"].isStruct())
|
if(!source["spellCast"].isNull() && source["spellCast"].isStruct())
|
||||||
@@ -293,7 +294,7 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto & artifact : info.reward.artifacts )
|
for (const auto & artifact : info.reward.grantedArtifacts )
|
||||||
{
|
{
|
||||||
loot.appendRawString("%s");
|
loot.appendRawString("%s");
|
||||||
loot.replaceName(artifact);
|
loot.replaceName(artifact);
|
||||||
@@ -315,7 +316,7 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const auto & artifact : info.reward.artifacts )
|
for (const auto & artifact : info.reward.grantedArtifacts )
|
||||||
target.replaceName(artifact);
|
target.replaceName(artifact);
|
||||||
|
|
||||||
for (const auto & spell : info.reward.spells )
|
for (const auto & spell : info.reward.spells )
|
||||||
|
@@ -151,16 +151,19 @@ void Rewardable::Interface::grantRewardAfterLevelup(const Rewardable::VisitInfo
|
|||||||
cb->setMovePoints(&smp);
|
cb->setMovePoints(&smp);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const Bonus & bonus : info.reward.bonuses)
|
for(const Bonus & bonus : info.reward.heroBonuses)
|
||||||
{
|
{
|
||||||
GiveBonus gb;
|
GiveBonus gb(GiveBonus::ETarget::OBJECT, hero->id, bonus);
|
||||||
gb.who = GiveBonus::ETarget::OBJECT;
|
|
||||||
gb.bonus = bonus;
|
|
||||||
gb.id = hero->id;
|
|
||||||
cb->giveHeroBonus(&gb);
|
cb->giveHeroBonus(&gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const ArtifactID & art : info.reward.artifacts)
|
for(const Bonus & bonus : info.reward.playerBonuses)
|
||||||
|
{
|
||||||
|
GiveBonus gb(GiveBonus::ETarget::PLAYER, hero->getOwner(), bonus);
|
||||||
|
cb->giveHeroBonus(&gb);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const ArtifactID & art : info.reward.grantedArtifacts)
|
||||||
cb->giveHeroNewArtifact(hero, art, ArtifactPosition::FIRST_AVAILABLE);
|
cb->giveHeroNewArtifact(hero, art, ArtifactPosition::FIRST_AVAILABLE);
|
||||||
|
|
||||||
if(!info.reward.spells.empty())
|
if(!info.reward.spells.empty())
|
||||||
|
@@ -48,6 +48,9 @@ struct DLL_LINKAGE Limiter final : public Serializeable
|
|||||||
/// Number of free secondary slots that hero needs to have
|
/// Number of free secondary slots that hero needs to have
|
||||||
bool canLearnSkills;
|
bool canLearnSkills;
|
||||||
|
|
||||||
|
/// Hero has commander, and commander is currently alive
|
||||||
|
bool commanderAlive;
|
||||||
|
|
||||||
/// resources player needs to have in order to trigger reward
|
/// resources player needs to have in order to trigger reward
|
||||||
TResources resources;
|
TResources resources;
|
||||||
|
|
||||||
@@ -59,6 +62,12 @@ struct DLL_LINKAGE Limiter final : public Serializeable
|
|||||||
/// checks for artifacts copies if same artifact id is included multiple times
|
/// checks for artifacts copies if same artifact id is included multiple times
|
||||||
std::vector<ArtifactID> artifacts;
|
std::vector<ArtifactID> artifacts;
|
||||||
|
|
||||||
|
/// artifact slots that hero needs to have available (not locked and without any artifact) to pass the limiter
|
||||||
|
std::vector<ArtifactPosition> availableSlots;
|
||||||
|
|
||||||
|
/// Spell scrolls that hero must have in inventory (equipped or in backpack)
|
||||||
|
std::vector<SpellID> scrolls;
|
||||||
|
|
||||||
/// Spells that hero must have in the spellbook
|
/// Spells that hero must have in the spellbook
|
||||||
std::vector<SpellID> spells;
|
std::vector<SpellID> spells;
|
||||||
|
|
||||||
@@ -102,10 +111,17 @@ struct DLL_LINKAGE Limiter final : public Serializeable
|
|||||||
h & manaPoints;
|
h & manaPoints;
|
||||||
h & manaPercentage;
|
h & manaPercentage;
|
||||||
h & canLearnSkills;
|
h & canLearnSkills;
|
||||||
|
if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS)
|
||||||
|
h & commanderAlive;
|
||||||
h & resources;
|
h & resources;
|
||||||
h & primary;
|
h & primary;
|
||||||
h & secondary;
|
h & secondary;
|
||||||
h & artifacts;
|
h & artifacts;
|
||||||
|
if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS)
|
||||||
|
{
|
||||||
|
h & availableSlots;
|
||||||
|
h & scrolls;
|
||||||
|
}
|
||||||
h & spells;
|
h & spells;
|
||||||
h & canLearnSpells;
|
h & canLearnSpells;
|
||||||
h & creatures;
|
h & creatures;
|
||||||
|
@@ -79,7 +79,7 @@ void Rewardable::Reward::loadComponents(std::vector<Component> & comps, const CG
|
|||||||
for (auto comp : extraComponents)
|
for (auto comp : extraComponents)
|
||||||
comps.push_back(comp);
|
comps.push_back(comp);
|
||||||
|
|
||||||
for (auto & bonus : bonuses)
|
for (auto & bonus : heroBonuses)
|
||||||
{
|
{
|
||||||
if (bonus.type == BonusType::MORALE)
|
if (bonus.type == BonusType::MORALE)
|
||||||
comps.emplace_back(ComponentType::MORALE, bonus.val);
|
comps.emplace_back(ComponentType::MORALE, bonus.val);
|
||||||
@@ -111,7 +111,7 @@ void Rewardable::Reward::loadComponents(std::vector<Component> & comps, const CG
|
|||||||
comps.emplace_back(ComponentType::SEC_SKILL, entry.first, finalLevel);
|
comps.emplace_back(ComponentType::SEC_SKILL, entry.first, finalLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto & entry : artifacts)
|
for(const auto & entry : grantedArtifacts)
|
||||||
comps.emplace_back(ComponentType::ARTIFACT, entry);
|
comps.emplace_back(ComponentType::ARTIFACT, entry);
|
||||||
|
|
||||||
for(const auto & entry : spells)
|
for(const auto & entry : spells)
|
||||||
@@ -141,7 +141,7 @@ void Rewardable::Reward::serializeJson(JsonSerializeFormat & handler)
|
|||||||
handler.serializeInt("manaDiff", manaDiff);
|
handler.serializeInt("manaDiff", manaDiff);
|
||||||
handler.serializeInt("manaOverflowFactor", manaOverflowFactor);
|
handler.serializeInt("manaOverflowFactor", manaOverflowFactor);
|
||||||
handler.serializeInt("movePoints", movePoints);
|
handler.serializeInt("movePoints", movePoints);
|
||||||
handler.serializeIdArray("artifacts", artifacts);
|
handler.serializeIdArray("artifacts", grantedArtifacts);
|
||||||
handler.serializeIdArray("spells", spells);
|
handler.serializeIdArray("spells", spells);
|
||||||
handler.enterArray("creatures").serializeStruct(creatures);
|
handler.enterArray("creatures").serializeStruct(creatures);
|
||||||
handler.enterArray("primary").serializeArray(primary);
|
handler.enterArray("primary").serializeArray(primary);
|
||||||
|
@@ -65,6 +65,8 @@ struct DLL_LINKAGE Reward final
|
|||||||
|
|
||||||
/// received experience
|
/// received experience
|
||||||
si32 heroExperience;
|
si32 heroExperience;
|
||||||
|
si32 commanderExperience;
|
||||||
|
si32 unitsExperience;
|
||||||
/// received levels (converted into XP during grant)
|
/// received levels (converted into XP during grant)
|
||||||
si32 heroLevel;
|
si32 heroLevel;
|
||||||
|
|
||||||
@@ -86,7 +88,8 @@ struct DLL_LINKAGE Reward final
|
|||||||
std::vector<CStackBasicDescriptor> guards;
|
std::vector<CStackBasicDescriptor> guards;
|
||||||
|
|
||||||
/// list of bonuses, e.g. morale/luck
|
/// list of bonuses, e.g. morale/luck
|
||||||
std::vector<Bonus> bonuses;
|
std::vector<Bonus> heroBonuses;
|
||||||
|
std::vector<Bonus> playerBonuses;
|
||||||
|
|
||||||
/// skills that hero may receive or lose
|
/// skills that hero may receive or lose
|
||||||
std::vector<si32> primary;
|
std::vector<si32> primary;
|
||||||
@@ -96,7 +99,10 @@ struct DLL_LINKAGE Reward final
|
|||||||
std::map<CreatureID, CreatureID> creaturesChange;
|
std::map<CreatureID, CreatureID> creaturesChange;
|
||||||
|
|
||||||
/// objects that hero may receive
|
/// objects that hero may receive
|
||||||
std::vector<ArtifactID> artifacts;
|
std::vector<ArtifactID> grantedArtifacts;
|
||||||
|
std::vector<ArtifactID> takenArtifacts;
|
||||||
|
std::vector<ArtifactPosition> takenArtifactSlots;
|
||||||
|
std::vector<SpellID> scrolls;
|
||||||
std::vector<SpellID> spells;
|
std::vector<SpellID> spells;
|
||||||
std::vector<CStackBasicDescriptor> creatures;
|
std::vector<CStackBasicDescriptor> creatures;
|
||||||
|
|
||||||
@@ -131,14 +137,29 @@ struct DLL_LINKAGE Reward final
|
|||||||
h & movePercentage;
|
h & movePercentage;
|
||||||
h & guards;
|
h & guards;
|
||||||
h & heroExperience;
|
h & heroExperience;
|
||||||
|
if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS)
|
||||||
|
{
|
||||||
|
h & commanderExperience;
|
||||||
|
h & unitsExperience;
|
||||||
|
}
|
||||||
h & heroLevel;
|
h & heroLevel;
|
||||||
h & manaDiff;
|
h & manaDiff;
|
||||||
h & manaOverflowFactor;
|
h & manaOverflowFactor;
|
||||||
h & movePoints;
|
h & movePoints;
|
||||||
h & primary;
|
h & primary;
|
||||||
h & secondary;
|
h & secondary;
|
||||||
h & bonuses;
|
h & heroBonuses;
|
||||||
h & artifacts;
|
if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS)
|
||||||
|
{
|
||||||
|
h & playerBonuses;
|
||||||
|
}
|
||||||
|
h & grantedArtifacts;
|
||||||
|
if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS)
|
||||||
|
{
|
||||||
|
h & takenArtifacts;
|
||||||
|
h & takenArtifactSlots;
|
||||||
|
h & scrolls;
|
||||||
|
}
|
||||||
h & spells;
|
h & spells;
|
||||||
h & creatures;
|
h & creatures;
|
||||||
h & creaturesChange;
|
h & creaturesChange;
|
||||||
|
@@ -39,9 +39,9 @@ enum class ESerializationVersion : int32_t
|
|||||||
STACK_INSTANCE_EXPERIENCE_FIX, // stack experience is stored as total, not as average
|
STACK_INSTANCE_EXPERIENCE_FIX, // stack experience is stored as total, not as average
|
||||||
STACK_INSTANCE_ARMY_FIX, // remove serialization of army that owns stack instance
|
STACK_INSTANCE_ARMY_FIX, // remove serialization of army that owns stack instance
|
||||||
STORE_UID_COUNTER_IN_CMAP, // fix crash caused by conflicting instanceName after loading game
|
STORE_UID_COUNTER_IN_CMAP, // fix crash caused by conflicting instanceName after loading game
|
||||||
|
REWARDABLE_EXTENSIONS, // new functionality for rewardable objects
|
||||||
|
|
||||||
|
CURRENT = REWARDABLE_EXTENSIONS,
|
||||||
CURRENT = STORE_UID_COUNTER_IN_CMAP,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");
|
static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");
|
||||||
|
@@ -297,11 +297,11 @@ void RewardsWidget::saveCurrentVisitInfo(int index)
|
|||||||
vinfo.reward.resources[i] = widget->value();
|
vinfo.reward.resources[i] = widget->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
vinfo.reward.artifacts.clear();
|
vinfo.reward.grantedArtifacts.clear();
|
||||||
for(int i = 0; i < ui->rArtifacts->count(); ++i)
|
for(int i = 0; i < ui->rArtifacts->count(); ++i)
|
||||||
{
|
{
|
||||||
if(ui->rArtifacts->item(i)->checkState() == Qt::Checked)
|
if(ui->rArtifacts->item(i)->checkState() == Qt::Checked)
|
||||||
vinfo.reward.artifacts.push_back(LIBRARY->artifacts()->getByIndex(i)->getId());
|
vinfo.reward.grantedArtifacts.push_back(LIBRARY->artifacts()->getByIndex(i)->getId());
|
||||||
}
|
}
|
||||||
vinfo.reward.spells.clear();
|
vinfo.reward.spells.clear();
|
||||||
for(int i = 0; i < ui->rSpells->count(); ++i)
|
for(int i = 0; i < ui->rSpells->count(); ++i)
|
||||||
@@ -336,13 +336,13 @@ void RewardsWidget::saveCurrentVisitInfo(int index)
|
|||||||
vinfo.reward.spellCast.second = ui->castLevel->currentIndex();
|
vinfo.reward.spellCast.second = ui->castLevel->currentIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
vinfo.reward.bonuses.clear();
|
vinfo.reward.heroBonuses.clear();
|
||||||
for(int i = 0; i < ui->bonuses->rowCount(); ++i)
|
for(int i = 0; i < ui->bonuses->rowCount(); ++i)
|
||||||
{
|
{
|
||||||
auto dur = bonusDurationMap.at(ui->bonuses->item(i, 0)->text().toStdString());
|
auto dur = bonusDurationMap.at(ui->bonuses->item(i, 0)->text().toStdString());
|
||||||
auto typ = bonusNameMap.at(ui->bonuses->item(i, 1)->text().toStdString());
|
auto typ = bonusNameMap.at(ui->bonuses->item(i, 1)->text().toStdString());
|
||||||
auto val = ui->bonuses->item(i, 2)->data(Qt::UserRole).toInt();
|
auto val = ui->bonuses->item(i, 2)->data(Qt::UserRole).toInt();
|
||||||
vinfo.reward.bonuses.emplace_back(dur, typ, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(object.id));
|
vinfo.reward.heroBonuses.emplace_back(dur, typ, BonusSource::OBJECT_INSTANCE, val, BonusSourceID(object.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
vinfo.limiter.dayOfWeek = ui->lDayOfWeek->currentIndex();
|
vinfo.limiter.dayOfWeek = ui->lDayOfWeek->currentIndex();
|
||||||
@@ -452,7 +452,7 @@ void RewardsWidget::loadCurrentVisitInfo(int index)
|
|||||||
widget->setValue(vinfo.reward.resources[i]);
|
widget->setValue(vinfo.reward.resources[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto i : vinfo.reward.artifacts)
|
for(auto i : vinfo.reward.grantedArtifacts)
|
||||||
ui->rArtifacts->item(LIBRARY->artifacts()->getById(i)->getIndex())->setCheckState(Qt::Checked);
|
ui->rArtifacts->item(LIBRARY->artifacts()->getById(i)->getIndex())->setCheckState(Qt::Checked);
|
||||||
for(auto i : vinfo.reward.spells)
|
for(auto i : vinfo.reward.spells)
|
||||||
ui->rArtifacts->item(LIBRARY->spells()->getById(i)->getIndex())->setCheckState(Qt::Checked);
|
ui->rArtifacts->item(LIBRARY->spells()->getById(i)->getIndex())->setCheckState(Qt::Checked);
|
||||||
@@ -478,7 +478,7 @@ void RewardsWidget::loadCurrentVisitInfo(int index)
|
|||||||
ui->castLevel->setCurrentIndex(vinfo.reward.spellCast.second);
|
ui->castLevel->setCurrentIndex(vinfo.reward.spellCast.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto & i : vinfo.reward.bonuses)
|
for(auto & i : vinfo.reward.heroBonuses)
|
||||||
{
|
{
|
||||||
auto dur = vstd::findKey(bonusDurationMap, i.duration);
|
auto dur = vstd::findKey(bonusDurationMap, i.duration);
|
||||||
for(int i = 0; i < ui->bonusDuration->count(); ++i)
|
for(int i = 0; i < ui->bonusDuration->count(); ++i)
|
||||||
@@ -786,7 +786,7 @@ void RewardsDelegate::updateModelData(QAbstractItemModel * model, const QModelIn
|
|||||||
}
|
}
|
||||||
textList += QObject::tr("Resources: %1").arg(resourcesList.join(", "));
|
textList += QObject::tr("Resources: %1").arg(resourcesList.join(", "));
|
||||||
QStringList artifactsList;
|
QStringList artifactsList;
|
||||||
for (auto artifact : vinfo.reward.artifacts)
|
for (auto artifact : vinfo.reward.grantedArtifacts)
|
||||||
{
|
{
|
||||||
artifactsList += QString::fromStdString(LIBRARY->artifacts()->getById(artifact)->getNameTranslated());
|
artifactsList += QString::fromStdString(LIBRARY->artifacts()->getById(artifact)->getNameTranslated());
|
||||||
}
|
}
|
||||||
@@ -814,7 +814,7 @@ void RewardsDelegate::updateModelData(QAbstractItemModel * model, const QModelIn
|
|||||||
textList += QObject::tr("Spell Cast: %1 (%2)").arg(QString::fromStdString(LIBRARY->spells()->getById(vinfo.reward.spellCast.first)->getNameTranslated())).arg(vinfo.reward.spellCast.second);
|
textList += QObject::tr("Spell Cast: %1 (%2)").arg(QString::fromStdString(LIBRARY->spells()->getById(vinfo.reward.spellCast.first)->getNameTranslated())).arg(vinfo.reward.spellCast.second);
|
||||||
}
|
}
|
||||||
QStringList bonusesList;
|
QStringList bonusesList;
|
||||||
for (auto & bonus : vinfo.reward.bonuses)
|
for (auto & bonus : vinfo.reward.heroBonuses)
|
||||||
{
|
{
|
||||||
bonusesList += QString("%1 %2 (%3)").arg(QString::fromStdString(vstd::findKey(bonusDurationMap, bonus.duration))).arg(QString::fromStdString(vstd::findKey(bonusNameMap, bonus.type))).arg(bonus.val);
|
bonusesList += QString("%1 %2 (%3)").arg(QString::fromStdString(vstd::findKey(bonusDurationMap, bonus.duration))).arg(QString::fromStdString(vstd::findKey(bonusNameMap, bonus.type))).arg(bonus.val);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user