1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-05-27 22:47:48 +02:00

Implemented basic version of configurable Witch Hut

This commit is contained in:
Ivan Savenko 2023-09-30 18:47:47 +03:00
parent 79b7518b0e
commit fd01a25352
28 changed files with 316 additions and 296 deletions

View File

@ -528,13 +528,16 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
} }
} }
float RewardEvaluator::evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role) const float RewardEvaluator::evaluateWitchHutSkillScore(const CGObjectInstance * hut, const CGHeroInstance * hero, HeroRole role) const
{ {
auto rewardable = dynamic_cast<const CRewardableObject *>(hut);
assert(rewardable);
auto skill = SecondarySkill(*rewardable->configuration.getVariable("secondarySkill", "gainedSkill"));
if(!hut->wasVisited(hero->tempOwner)) if(!hut->wasVisited(hero->tempOwner))
return role == HeroRole::SCOUT ? 2 : 0; return role == HeroRole::SCOUT ? 2 : 0;
auto skill = SecondarySkill(hut->ability);
if(hero->getSecSkillLevel(skill) != SecSkillLevel::NONE if(hero->getSecSkillLevel(skill) != SecSkillLevel::NONE
|| hero->secSkills.size() >= GameConstants::SKILL_PER_HERO) || hero->secSkills.size() >= GameConstants::SKILL_PER_HERO)
return 0; return 0;
@ -575,7 +578,7 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
case Obj::LIBRARY_OF_ENLIGHTENMENT: case Obj::LIBRARY_OF_ENLIGHTENMENT:
return 8; return 8;
case Obj::WITCH_HUT: case Obj::WITCH_HUT:
return evaluateWitchHutSkillScore(dynamic_cast<const CGWitchHut *>(target), hero, role); return evaluateWitchHutSkillScore(target, hero, role);
case Obj::PANDORAS_BOX: case Obj::PANDORAS_BOX:
//Can contains experience, spells, or skills (only on custom maps) //Can contains experience, spells, or skills (only on custom maps)
return 2.5f; return 2.5f;

View File

@ -18,8 +18,6 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CGWitchHut;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
namespace NKAI namespace NKAI
@ -43,7 +41,7 @@ public:
float getResourceRequirementStrength(int resType) const; float getResourceRequirementStrength(int resType) const;
float getStrategicalValue(const CGObjectInstance * target) const; float getStrategicalValue(const CGObjectInstance * target) const;
float getTotalResourceRequirementStrength(int resType) const; float getTotalResourceRequirementStrength(int resType) const;
float evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role) const; float evaluateWitchHutSkillScore(const CGObjectInstance * hut, const CGHeroInstance * hero, HeroRole role) const;
float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const; float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const;
int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const; int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const;
uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const; uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const;

View File

@ -90,7 +90,7 @@ Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o
{ {
auto allResources = cb->getResourceAmount(); auto allResources = cb->getResourceAmount();
auto income = estimateIncome(); auto income = estimateIncome();
GameResID resourceType = EGameResID::INVALID; GameResID resourceType = EGameResID::NONE;
TResource amountToCollect = 0; TResource amountToCollect = 0;
using resPair = std::pair<GameResID, TResource>; using resPair = std::pair<GameResID, TResource>;
@ -129,7 +129,7 @@ Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o
break; break;
} }
} }
if (resourceType == EGameResID::INVALID) //no needed resources has 0 income, if (resourceType == EGameResID::NONE) //no needed resources has 0 income,
{ {
//find the one which takes longest to collect //find the one which takes longest to collect
using timePair = std::pair<GameResID, float>; using timePair = std::pair<GameResID, float>;

View File

@ -52,6 +52,7 @@
"config/objects/moddables.json", "config/objects/moddables.json",
"config/objects/creatureBanks.json", "config/objects/creatureBanks.json",
"config/objects/dwellings.json", "config/objects/dwellings.json",
"config/objects/rewardableGeneric.json",
"config/objects/rewardableOncePerWeek.json", "config/objects/rewardableOncePerWeek.json",
"config/objects/rewardablePickable.json", "config/objects/rewardablePickable.json",
"config/objects/rewardableOnceVisitable.json", "config/objects/rewardableOnceVisitable.json",

View File

@ -587,26 +587,6 @@
} }
} }
}, },
"witchHut" : {
"index" :113,
"handler" : "witch",
"base" : {
"sounds" : {
"visit" : ["GAZEBO"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"zoneLimit" : 3,
"value" : 1500,
"rarity" : 80
}
}
}
},
"questGuard" : { "questGuard" : {
"index" :215, "index" :215,
"handler" : "questGuard", "handler" : "questGuard",

View File

@ -220,7 +220,7 @@ Keep in mind, that all randomization is performed on map load and on object rese
```jsonc ```jsonc
"resources": [ "resources": [
{ {
"list" : [ "wood", "ore" ], "anyOf" : [ "wood", "ore" ],
"amount" : 10 "amount" : 10
}, },
{ {

View File

@ -48,7 +48,7 @@ private:
std::string identifier; std::string identifier;
public: public:
CSkill(const SecondarySkill & id = SecondarySkill::DEFAULT, std::string identifier = "default", bool obligatoryMajor = false, bool obligatoryMinor = false); CSkill(const SecondarySkill & id = SecondarySkill::NONE, std::string identifier = "default", bool obligatoryMajor = false, bool obligatoryMinor = false);
~CSkill() = default; ~CSkill() = default;
enum class Obligatory : ui8 enum class Obligatory : ui8

View File

@ -32,38 +32,71 @@ VCMI_LIB_NAMESPACE_BEGIN
namespace JsonRandom namespace JsonRandom
{ {
si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue) si32 loadVariable(std::string variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
{
if (value.empty() || value[0] != '@')
{
logMod->warn("Invalid syntax in load value! Can not load value from '%s'", value);
return defaultValue;
}
std::string variableID = variableGroup + value;
if (variables.count(variableID) == 0)
{
logMod->warn("Invalid syntax in load value! Unknown variable '%s'", value);
return defaultValue;
}
return variables.at(variableID);
}
si32 loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue)
{ {
if(value.isNull()) if(value.isNull())
return defaultValue; return defaultValue;
if(value.isNumber()) if(value.isNumber())
return static_cast<si32>(value.Float()); return static_cast<si32>(value.Float());
if (value.isString())
return loadVariable("number", value.String(), variables, defaultValue);
if(value.isVector()) if(value.isVector())
{ {
const auto & vector = value.Vector(); const auto & vector = value.Vector();
size_t index= rng.getIntRange(0, vector.size()-1)(); size_t index= rng.getIntRange(0, vector.size()-1)();
return loadValue(vector[index], rng, 0); return loadValue(vector[index], rng, variables, 0);
} }
if(value.isStruct()) if(value.isStruct())
{ {
if (!value["amount"].isNull()) if (!value["amount"].isNull())
return static_cast<si32>(loadValue(value["amount"], rng, defaultValue)); return static_cast<si32>(loadValue(value["amount"], rng, variables, defaultValue));
si32 min = static_cast<si32>(loadValue(value["min"], rng, 0)); si32 min = static_cast<si32>(loadValue(value["min"], rng, variables, 0));
si32 max = static_cast<si32>(loadValue(value["max"], rng, 0)); si32 max = static_cast<si32>(loadValue(value["max"], rng, variables, 0));
return rng.getIntRange(min, max)(); return rng.getIntRange(min, max)();
} }
return defaultValue; return defaultValue;
} }
template<typename IdentifierType> template<typename IdentifierType>
IdentifierType decodeKey(const JsonNode & value) IdentifierType decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
{ {
if (value.empty() || value[0] != '@')
return IdentifierType(*VLC->identifiers()->getIdentifier(modScope, IdentifierType::entityType(), value));
else
return loadVariable(IdentifierType::entityType(), value, variables, IdentifierType::NONE);
}
template<typename IdentifierType>
IdentifierType decodeKey(const JsonNode & value, const Variables & variables)
{
if (value.String().empty() || value.String()[0] != '@')
return IdentifierType(*VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value)); return IdentifierType(*VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value));
else
return loadVariable(IdentifierType::entityType(), value.String(), variables, IdentifierType::NONE);
} }
template<> template<>
PrimarySkill decodeKey(const JsonNode & value) PrimarySkill decodeKey(const JsonNode & value, const Variables & variables)
{ {
return PrimarySkill(*VLC->identifiers()->getIdentifier("primarySkill", value)); return PrimarySkill(*VLC->identifiers()->getIdentifier("primarySkill", value));
} }
@ -143,7 +176,7 @@ namespace JsonRandom
if (!value["level"].isNull()) if (!value["level"].isNull())
{ {
int32_t spellLevel = value["level"].Float(); int32_t spellLevel = value["level"].Integer();
vstd::erase_if(result, [=](const SpellID & spell) vstd::erase_if(result, [=](const SpellID & spell)
{ {
@ -164,17 +197,17 @@ namespace JsonRandom
} }
template<typename IdentifierType> template<typename IdentifierType>
std::set<IdentifierType> filterKeys(const JsonNode & value, const std::set<IdentifierType> & valuesSet) std::set<IdentifierType> filterKeys(const JsonNode & value, const std::set<IdentifierType> & valuesSet, const Variables & variables)
{ {
if(value.isString()) if(value.isString())
return { decodeKey<IdentifierType>(value) }; return { decodeKey<IdentifierType>(value, variables) };
assert(value.isStruct()); assert(value.isStruct());
if(value.isStruct()) if(value.isStruct())
{ {
if(!value["type"].isNull()) if(!value["type"].isNull())
return filterKeys(value["type"], valuesSet); return filterKeys(value["type"], valuesSet, variables);
std::set<IdentifierType> filteredTypes = filterKeysTyped(value, valuesSet); std::set<IdentifierType> filteredTypes = filterKeysTyped(value, valuesSet);
@ -183,7 +216,7 @@ namespace JsonRandom
std::set<IdentifierType> filteredAnyOf; std::set<IdentifierType> filteredAnyOf;
for (auto const & entry : value["anyOf"].Vector()) for (auto const & entry : value["anyOf"].Vector())
{ {
std::set<IdentifierType> subset = filterKeys(entry, valuesSet); std::set<IdentifierType> subset = filterKeys(entry, valuesSet, variables);
filteredAnyOf.insert(subset.begin(), subset.end()); filteredAnyOf.insert(subset.begin(), subset.end());
} }
@ -197,7 +230,7 @@ namespace JsonRandom
{ {
for (auto const & entry : value["noneOf"].Vector()) for (auto const & entry : value["noneOf"].Vector())
{ {
std::set<IdentifierType> subset = filterKeys(entry, valuesSet); std::set<IdentifierType> subset = filterKeys(entry, valuesSet, variables);
for (auto bannedEntry : subset ) for (auto bannedEntry : subset )
filteredTypes.erase(bannedEntry); filteredTypes.erase(bannedEntry);
} }
@ -208,25 +241,25 @@ namespace JsonRandom
return valuesSet; return valuesSet;
} }
TResources loadResources(const JsonNode & value, CRandomGenerator & rng) TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
TResources ret; TResources ret;
if (value.isVector()) if (value.isVector())
{ {
for (const auto & entry : value.Vector()) for (const auto & entry : value.Vector())
ret += loadResource(entry, rng); ret += loadResource(entry, rng, variables);
return ret; return ret;
} }
for (size_t i=0; i<GameConstants::RESOURCE_QUANTITY; i++) for (size_t i=0; i<GameConstants::RESOURCE_QUANTITY; i++)
{ {
ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], rng); ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], rng, variables);
} }
return ret; return ret;
} }
TResources loadResource(const JsonNode & value, CRandomGenerator & rng) TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::set<GameResID> defaultResources{ std::set<GameResID> defaultResources{
GameResID::WOOD, GameResID::WOOD,
@ -238,9 +271,9 @@ namespace JsonRandom
GameResID::GOLD GameResID::GOLD
}; };
std::set<GameResID> potentialPicks = filterKeys(value, defaultResources); std::set<GameResID> potentialPicks = filterKeys(value, defaultResources, variables);
GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng); GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
si32 resourceAmount = loadValue(value, rng, 0); si32 resourceAmount = loadValue(value, rng, variables, 0);
TResources ret; TResources ret;
ret[resourceID] = resourceAmount; ret[resourceID] = resourceAmount;
@ -248,14 +281,14 @@ namespace JsonRandom
} }
std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng) std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::vector<si32> ret; std::vector<si32> ret;
if(value.isStruct()) if(value.isStruct())
{ {
for(const auto & name : NPrimarySkill::names) for(const auto & name : NPrimarySkill::names)
{ {
ret.push_back(loadValue(value[name], rng)); ret.push_back(loadValue(value[name], rng, variables));
} }
} }
if(value.isVector()) if(value.isVector())
@ -271,25 +304,36 @@ namespace JsonRandom
for(const auto & element : value.Vector()) for(const auto & element : value.Vector())
{ {
std::set<PrimarySkill> potentialPicks = filterKeys(element, defaultSkills); std::set<PrimarySkill> potentialPicks = filterKeys(element, defaultSkills, variables);
PrimarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng); PrimarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
defaultSkills.erase(skillID); defaultSkills.erase(skillID);
ret[static_cast<int>(skillID)] += loadValue(element, rng); ret[static_cast<int>(skillID)] += loadValue(element, rng, variables);
} }
} }
return ret; return ret;
} }
std::map<SecondarySkill, si32> loadSecondary(const JsonNode & value, CRandomGenerator & rng) SecondarySkill loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{
std::set<SecondarySkill> defaultSkills;
for(const auto & skill : VLC->skillh->objects)
if (IObjectInterface::cb->isAllowed(2, skill->getIndex()))
defaultSkills.insert(skill->getId());
std::set<SecondarySkill> potentialPicks = filterKeys(value, defaultSkills, variables);
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
}
std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::map<SecondarySkill, si32> ret; std::map<SecondarySkill, si32> ret;
if(value.isStruct()) if(value.isStruct())
{ {
for(const auto & pair : value.Struct()) for(const auto & pair : value.Struct())
{ {
SecondarySkill id(VLC->identifiers()->getIdentifier(pair.second.meta, "skill", pair.first).value()); SecondarySkill id = decodeKey<SecondarySkill>(pair.second.meta, pair.first, variables);
ret[id] = loadValue(pair.second, rng); ret[id] = loadValue(pair.second, rng, variables);
} }
} }
if(value.isVector()) if(value.isVector())
@ -301,45 +345,45 @@ namespace JsonRandom
for(const auto & element : value.Vector()) for(const auto & element : value.Vector())
{ {
std::set<SecondarySkill> potentialPicks = filterKeys(element, defaultSkills); std::set<SecondarySkill> potentialPicks = filterKeys(element, defaultSkills, variables);
SecondarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng); SecondarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
defaultSkills.erase(skillID); //avoid dupicates defaultSkills.erase(skillID); //avoid dupicates
ret[skillID] = loadValue(element, rng); ret[skillID] = loadValue(element, rng, variables);
} }
} }
return ret; return ret;
} }
ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng) ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::set<ArtifactID> allowedArts; std::set<ArtifactID> allowedArts;
for (auto const * artifact : VLC->arth->allowedArtifacts) for (auto const * artifact : VLC->arth->allowedArtifacts)
allowedArts.insert(artifact->getId()); allowedArts.insert(artifact->getId());
std::set<ArtifactID> potentialPicks = filterKeys(value, allowedArts); std::set<ArtifactID> potentialPicks = filterKeys(value, allowedArts, variables);
return VLC->arth->pickRandomArtifact(rng, potentialPicks); return VLC->arth->pickRandomArtifact(rng, potentialPicks);
} }
std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng) std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::vector<ArtifactID> ret; std::vector<ArtifactID> ret;
for (const JsonNode & entry : value.Vector()) for (const JsonNode & entry : value.Vector())
{ {
ret.push_back(loadArtifact(entry, rng)); ret.push_back(loadArtifact(entry, rng, variables));
} }
return ret; return ret;
} }
SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> spells) SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::set<SpellID> defaultSpells; std::set<SpellID> defaultSpells;
for(const auto & spell : VLC->spellh->objects) for(const auto & spell : VLC->spellh->objects)
if (IObjectInterface::cb->isAllowed(0, spell->getIndex())) if (IObjectInterface::cb->isAllowed(0, spell->getIndex()))
defaultSpells.insert(spell->getId()); defaultSpells.insert(spell->getId());
std::set<SpellID> potentialPicks = filterKeys(value, defaultSpells); std::set<SpellID> potentialPicks = filterKeys(value, defaultSpells, variables);
if (potentialPicks.empty()) if (potentialPicks.empty())
{ {
@ -349,12 +393,12 @@ namespace JsonRandom
return *RandomGeneratorUtil::nextItem(potentialPicks, rng); return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
} }
std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const std::vector<SpellID> & spells) std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::vector<SpellID> ret; std::vector<SpellID> ret;
for (const JsonNode & entry : value.Vector()) for (const JsonNode & entry : value.Vector())
{ {
ret.push_back(loadSpell(entry, rng, spells)); ret.push_back(loadSpell(entry, rng, variables));
} }
return ret; return ret;
} }
@ -399,7 +443,7 @@ namespace JsonRandom
return ret; return ret;
} }
CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng) CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
CStackBasicDescriptor stack; CStackBasicDescriptor stack;
@ -408,7 +452,7 @@ namespace JsonRandom
if (!creature->special) if (!creature->special)
defaultCreatures.insert(creature->getId()); defaultCreatures.insert(creature->getId());
std::set<CreatureID> potentialPicks = filterKeys(value, defaultCreatures); std::set<CreatureID> potentialPicks = filterKeys(value, defaultCreatures, variables);
CreatureID pickedCreature; CreatureID pickedCreature;
if (!potentialPicks.empty()) if (!potentialPicks.empty())
@ -417,7 +461,7 @@ namespace JsonRandom
logMod->warn("Failed to select suitable random creature!"); logMod->warn("Failed to select suitable random creature!");
stack.type = VLC->creh->objects[pickedCreature]; stack.type = VLC->creh->objects[pickedCreature];
stack.count = loadValue(value, rng); stack.count = loadValue(value, rng, variables);
if (!value["upgradeChance"].isNull() && !stack.type->upgrades.empty()) if (!value["upgradeChance"].isNull() && !stack.type->upgrades.empty())
{ {
if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade
@ -428,17 +472,17 @@ namespace JsonRandom
return stack; return stack;
} }
std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng) std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{ {
std::vector<CStackBasicDescriptor> ret; std::vector<CStackBasicDescriptor> ret;
for (const JsonNode & node : value.Vector()) for (const JsonNode & node : value.Vector())
{ {
ret.push_back(loadCreature(node, rng)); ret.push_back(loadCreature(node, rng, variables));
} }
return ret; return ret;
} }
std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value) std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables)
{ {
std::vector<RandomStackInfo> ret; std::vector<RandomStackInfo> ret;
for (const JsonNode & node : value.Vector()) for (const JsonNode & node : value.Vector())
@ -464,13 +508,6 @@ namespace JsonRandom
return ret; return ret;
} }
//std::vector<Component> loadComponents(const JsonNode & value)
//{
// std::vector<Component> ret;
// return ret;
// //TODO
//}
std::vector<Bonus> DLL_LINKAGE loadBonuses(const JsonNode & value) std::vector<Bonus> DLL_LINKAGE loadBonuses(const JsonNode & value)
{ {
std::vector<Bonus> ret; std::vector<Bonus> ret;

View File

@ -24,6 +24,8 @@ class CStackBasicDescriptor;
namespace JsonRandom namespace JsonRandom
{ {
using Variables = std::map<std::string, int>;
struct DLL_LINKAGE RandomStackInfo struct DLL_LINKAGE RandomStackInfo
{ {
std::vector<const CCreature *> allowedCreatures; std::vector<const CCreature *> allowedCreatures;
@ -31,29 +33,29 @@ namespace JsonRandom
si32 maxAmount; si32 maxAmount;
}; };
DLL_LINKAGE si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue = 0); DLL_LINKAGE si32 loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue = 0);
DLL_LINKAGE TResources loadResources(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE TResources loadResource(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::map<SecondarySkill, si32> loadSecondary(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE SecondarySkill loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> spells = {}); DLL_LINKAGE SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const std::vector<SpellID> & spells = {}); DLL_LINKAGE std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value); DLL_LINKAGE std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables);
DLL_LINKAGE std::vector<PlayerColor> loadColors(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE std::vector<PlayerColor> loadColors(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::vector<HeroTypeID> loadHeroes(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE std::vector<HeroTypeID> loadHeroes(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, CRandomGenerator & rng); DLL_LINKAGE std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::vector<Bonus> loadBonuses(const JsonNode & value); DLL_LINKAGE std::vector<Bonus> loadBonuses(const JsonNode & value);
//DLL_LINKAGE std::vector<Component> loadComponents(const JsonNode & value);
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -308,8 +308,7 @@ class SecondarySkillBase : public IdentifierBase
public: public:
enum Type : int32_t enum Type : int32_t
{ {
WRONG = -2, NONE = -1,
DEFAULT = -1,
PATHFINDING = 0, PATHFINDING = 0,
ARCHERY, ARCHERY,
LOGISTICS, LOGISTICS,
@ -938,7 +937,7 @@ public:
COUNT, COUNT,
WOOD_AND_ORE = 127, // special case for town bonus resource WOOD_AND_ORE = 127, // special case for town bonus resource
INVALID = -1 NONE = -1
}; };
}; };

View File

@ -37,17 +37,15 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const
{ {
BankConfig bc; BankConfig bc;
JsonRandom::Variables emptyVariables;
bc.chance = static_cast<ui32>(level["chance"].Float()); bc.chance = static_cast<ui32>(level["chance"].Float());
bc.guards = JsonRandom::loadCreatures(level["guards"], rng); bc.guards = JsonRandom::loadCreatures(level["guards"], rng, emptyVariables);
std::vector<SpellID> spells;
IObjectInterface::cb->getAllowedSpells(spells);
bc.resources = ResourceSet(level["reward"]["resources"]); bc.resources = ResourceSet(level["reward"]["resources"]);
bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng); bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng, emptyVariables);
bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng); bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng, emptyVariables);
bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells); bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, emptyVariables);
return bc; return bc;
} }
@ -105,10 +103,12 @@ static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature *
IObjectInfo::CArmyStructure CBankInfo::minGuards() const IObjectInfo::CArmyStructure CBankInfo::minGuards() const
{ {
JsonRandom::Variables emptyVariables;
std::vector<IObjectInfo::CArmyStructure> armies; std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config) for(auto configEntry : config)
{ {
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"], emptyVariables);
IObjectInfo::CArmyStructure army; IObjectInfo::CArmyStructure army;
for(auto & stack : stacks) for(auto & stack : stacks)
{ {
@ -126,10 +126,12 @@ IObjectInfo::CArmyStructure CBankInfo::minGuards() const
IObjectInfo::CArmyStructure CBankInfo::maxGuards() const IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
{ {
JsonRandom::Variables emptyVariables;
std::vector<IObjectInfo::CArmyStructure> armies; std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config) for(auto configEntry : config)
{ {
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"], emptyVariables);
IObjectInfo::CArmyStructure army; IObjectInfo::CArmyStructure army;
for(auto & stack : stacks) for(auto & stack : stacks)
{ {
@ -147,12 +149,13 @@ IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
TPossibleGuards CBankInfo::getPossibleGuards() const TPossibleGuards CBankInfo::getPossibleGuards() const
{ {
JsonRandom::Variables emptyVariables;
TPossibleGuards out; TPossibleGuards out;
for(const JsonNode & configEntry : config) for(const JsonNode & configEntry : config)
{ {
const JsonNode & guardsInfo = configEntry["guards"]; const JsonNode & guardsInfo = configEntry["guards"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo); auto stacks = JsonRandom::evaluateCreatures(guardsInfo, emptyVariables);
IObjectInfo::CArmyStructure army; IObjectInfo::CArmyStructure army;
@ -187,12 +190,13 @@ std::vector<PossibleReward<TResources>> CBankInfo::getPossibleResourcesReward()
std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const
{ {
JsonRandom::Variables emptyVariables;
std::vector<PossibleReward<CStackBasicDescriptor>> aproximateReward; std::vector<PossibleReward<CStackBasicDescriptor>> aproximateReward;
for(const JsonNode & configEntry : config) for(const JsonNode & configEntry : config)
{ {
const JsonNode & guardsInfo = configEntry["reward"]["creatures"]; const JsonNode & guardsInfo = configEntry["reward"]["creatures"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo); auto stacks = JsonRandom::evaluateCreatures(guardsInfo, emptyVariables);
for(auto stack : stacks) for(auto stack : stacks)
{ {

View File

@ -95,7 +95,6 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("monolith", CGMonolith); SET_HANDLER("monolith", CGMonolith);
SET_HANDLER("subterraneanGate", CGSubterraneanGate); SET_HANDLER("subterraneanGate", CGSubterraneanGate);
SET_HANDLER("whirlpool", CGWhirlpool); SET_HANDLER("whirlpool", CGWhirlpool);
SET_HANDLER("witch", CGWitchHut);
SET_HANDLER("terrain", CGTerrainPatch); SET_HANDLER("terrain", CGTerrainPatch);
#undef SET_HANDLER_CLASS #undef SET_HANDLER_CLASS

View File

@ -35,7 +35,6 @@ public:
{ {
AObjectTypeHandler::serialize(h, version); AObjectTypeHandler::serialize(h, version);
if (version >= 816)
h & objectInfo; h & objectInfo;
} }
}; };

View File

@ -256,9 +256,11 @@ void MarketInstanceConstructor::initializeObject(CGMarket * market) const
void MarketInstanceConstructor::randomizeObject(CGMarket * object, CRandomGenerator & rng) const void MarketInstanceConstructor::randomizeObject(CGMarket * object, CRandomGenerator & rng) const
{ {
JsonRandom::Variables emptyVariables;
if(auto * university = dynamic_cast<CGUniversity *>(object)) if(auto * university = dynamic_cast<CGUniversity *>(object))
{ {
for(auto skill : JsonRandom::loadSecondary(predefinedOffer, rng)) for(auto skill : JsonRandom::loadSecondaries(predefinedOffer, rng, emptyVariables))
university->skills.push_back(skill.first.getNum()); university->skills.push_back(skill.first.getNum());
} }
} }

View File

@ -93,7 +93,8 @@ void DwellingInstanceConstructor::randomizeObject(CGDwelling * object, CRandomGe
} }
else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux) else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux)
{ {
for(auto & stack : JsonRandom::loadCreatures(guards, rng)) JsonRandom::Variables emptyVariables;
for(auto & stack : JsonRandom::loadCreatures(guards, rng, emptyVariables))
{ {
dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->getId(), stack.count)); dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->getId(), stack.count));
} }

View File

@ -23,6 +23,8 @@ void ShrineInstanceConstructor::initTypeData(const JsonNode & config)
void ShrineInstanceConstructor::randomizeObject(CGShrine * shrine, CRandomGenerator & rng) const void ShrineInstanceConstructor::randomizeObject(CGShrine * shrine, CRandomGenerator & rng) const
{ {
JsonRandom::Variables emptyVariables;
auto visitTextParameter = parameters["visitText"]; auto visitTextParameter = parameters["visitText"];
if (visitTextParameter.isNumber()) if (visitTextParameter.isNumber())
@ -31,12 +33,7 @@ void ShrineInstanceConstructor::randomizeObject(CGShrine * shrine, CRandomGenera
shrine->visitText.appendRawString(visitTextParameter.String()); shrine->visitText.appendRawString(visitTextParameter.String());
if(shrine->spell == SpellID::NONE) // shrine has no predefined spell if(shrine->spell == SpellID::NONE) // shrine has no predefined spell
{ shrine->spell =JsonRandom::loadSpell(parameters["spell"], rng, emptyVariables);
std::vector<SpellID> possibilities;
shrine->cb->getAllowedSpells(possibilities);
shrine->spell =JsonRandom::loadSpell(parameters["spell"], rng, possibilities);
}
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -253,7 +253,7 @@ CGHeroInstance::CGHeroInstance():
{ {
setNodeType(HERO); setNodeType(HERO);
ID = Obj::HERO; ID = Obj::HERO;
secSkills.emplace_back(SecondarySkill::DEFAULT, -1); secSkills.emplace_back(SecondarySkill::NONE, -1);
blockVisit = true; blockVisit = true;
} }
@ -315,7 +315,7 @@ void CGHeroInstance::initHero(CRandomGenerator & rand)
pushPrimSkill(static_cast<PrimarySkill>(g), type->heroClass->primarySkillInitial[g]); pushPrimSkill(static_cast<PrimarySkill>(g), type->heroClass->primarySkillInitial[g]);
} }
} }
if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::DEFAULT, -1)) //set secondary skills to default if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::NONE, -1)) //set secondary skills to default
secSkills = type->secSkillsInit; secSkills = type->secSkillsInit;
if (gender == EHeroGender::DEFAULT) if (gender == EHeroGender::DEFAULT)
@ -1580,7 +1580,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
bool normalSkills = false; bool normalSkills = false;
for(const auto & p : secSkills) for(const auto & p : secSkills)
{ {
if(p.first == SecondarySkill(SecondarySkill::DEFAULT)) if(p.first == SecondarySkill(SecondarySkill::NONE))
defaultSkills = true; defaultSkills = true;
else else
normalSkills = true; normalSkills = true;
@ -1618,7 +1618,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
secSkills.clear(); secSkills.clear();
if(secondarySkills.getType() == JsonNode::JsonType::DATA_NULL) if(secondarySkills.getType() == JsonNode::JsonType::DATA_NULL)
{ {
secSkills.emplace_back(SecondarySkill::DEFAULT, -1); secSkills.emplace_back(SecondarySkill::NONE, -1);
} }
else else
{ {

View File

@ -90,7 +90,6 @@ public:
// POSSIBLE // POSSIBLE
// class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles // class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
// class DLL_LINKAGE CGWitchHut : public CPlayersVisited
// class DLL_LINKAGE CGScholar : public CGObjectInstance // class DLL_LINKAGE CGScholar : public CGObjectInstance
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -842,98 +842,6 @@ void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
} }
} }
void CGWitchHut::initObj(CRandomGenerator & rand)
{
if (allowedAbilities.empty()) //this can happen for RMG and RoE maps.
{
auto defaultAllowed = VLC->skillh->getDefaultAllowed();
// Necromancy and Leadership can't be learned by default
defaultAllowed[SecondarySkill::NECROMANCY] = false;
defaultAllowed[SecondarySkill::LEADERSHIP] = false;
for(int i = 0; i < defaultAllowed.size(); i++)
if (defaultAllowed[i] && cb->isAllowed(2, i))
allowedAbilities.insert(SecondarySkill(i));
}
ability = *RandomGeneratorUtil::nextItem(allowedAbilities, rand);
}
void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
{
InfoWindow iw;
iw.type = EInfoWindowMode::AUTO;
iw.player = h->getOwner();
if(!wasVisited(h->tempOwner))
cb->setObjProperty(id, CGWitchHut::OBJPROP_VISITED, h->tempOwner.getNum());
ui32 txt_id;
if(h->getSecSkillLevel(SecondarySkill(ability))) //you already know this skill
{
txt_id =172;
}
else if(!h->canLearnSkill()) //already all skills slots used
{
txt_id = 173;
}
else //give sec skill
{
iw.components.emplace_back(Component::EComponentType::SEC_SKILL, ability, 1, 0);
txt_id = 171;
cb->changeSecSkill(h, SecondarySkill(ability), 1, true);
}
iw.text.appendLocalString(EMetaText::ADVOB_TXT,txt_id);
iw.text.replaceLocalString(EMetaText::SEC_SKILL_NAME, ability);
cb->showInfoDialog(&iw);
}
std::string CGWitchHut::getHoverText(PlayerColor player) const
{
std::string hoverName = getObjectName();
if(wasVisited(player))
{
hoverName += "\n" + VLC->generaltexth->allTexts[356]; // + (learn %s)
boost::algorithm::replace_first(hoverName, "%s", VLC->skillh->getByIndex(ability)->getNameTranslated());
}
return hoverName;
}
std::string CGWitchHut::getHoverText(const CGHeroInstance * hero) const
{
std::string hoverName = getHoverText(hero->tempOwner);
if(wasVisited(hero->tempOwner) && hero->getSecSkillLevel(SecondarySkill(ability))) //hero knows that ability
hoverName += "\n\n" + VLC->generaltexth->allTexts[357]; // (Already learned)
return hoverName;
}
void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler)
{
//TODO: unify allowed abilities with others - make them std::vector<bool>
std::vector<bool> temp;
size_t skillCount = VLC->skillh->size();
temp.resize(skillCount, false);
auto standard = VLC->skillh->getDefaultAllowed(); //todo: for WitchHut default is all except Leadership and Necromancy
if(handler.saving)
{
for(SecondarySkill i(0); i < SecondarySkill(skillCount); ++i)
if(vstd::contains(allowedAbilities, i))
temp[i] = true;
}
handler.serializeLIC("allowedSkills", &CSkillHandler::decodeSkill, &CSkillHandler::encodeSkill, standard, temp);
if(!handler.saving)
{
allowedAbilities.clear();
for(si32 i = 0; i < skillCount; ++i)
if(temp[i])
allowedAbilities.insert(SecondarySkill(i));
}
}
void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
{ {
InfoWindow iw; InfoWindow iw;

View File

@ -57,26 +57,6 @@ protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override; void serializeJsonOptions(JsonSerializeFormat & handler) override;
}; };
class DLL_LINKAGE CGWitchHut : public CTeamVisited
{
public:
std::set<SecondarySkill> allowedAbilities;
SecondarySkill ability;
std::string getHoverText(PlayerColor player) const override;
std::string getHoverText(const CGHeroInstance * hero) const override;
void onHeroVisit(const CGHeroInstance * h) const override;
void initObj(CRandomGenerator & rand) override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CTeamVisited&>(*this);
h & allowedAbilities;
h & ability;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGScholar : public CGObjectInstance class DLL_LINKAGE CGScholar : public CGObjectInstance
{ {
public: public:

View File

@ -1139,23 +1139,40 @@ CGObjectInstance * CMapLoaderH3M::readSign(const int3 & mapPosition)
return object; return object;
} }
CGObjectInstance * CMapLoaderH3M::readWitchHut() CGObjectInstance * CMapLoaderH3M::readWitchHut(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate)
{ {
auto * object = new CGWitchHut(); auto * object = readGeneric(position, objectTemplate);
auto * rewardable = dynamic_cast<CRewardableObject*>(object);
assert(rewardable);
// AB and later maps have allowed abilities defined in H3M // AB and later maps have allowed abilities defined in H3M
if(features.levelAB) if(features.levelAB)
{ {
reader->readBitmaskSkills(object->allowedAbilities, false); std::set<SecondarySkill> allowedAbilities;
reader->readBitmaskSkills(allowedAbilities, false);
if(object->allowedAbilities.size() != 1) if(allowedAbilities.size() != 1)
{ {
auto defaultAllowed = VLC->skillh->getDefaultAllowed(); auto defaultAllowed = VLC->skillh->getDefaultAllowed();
for(int skillID = 0; skillID < VLC->skillh->size(); ++skillID) for(int skillID = 0; skillID < VLC->skillh->size(); ++skillID)
if(defaultAllowed[skillID]) if(defaultAllowed[skillID])
object->allowedAbilities.insert(SecondarySkill(skillID)); allowedAbilities.insert(SecondarySkill(skillID));
} }
JsonVector anyOfList;
for (auto const & skill : allowedAbilities)
{
JsonNode entry;
entry.String() = VLC->skills()->getById(skill)->getJsonKey();
anyOfList.push_back(entry);
}
JsonNode variable;
variable.Vector() = anyOfList;
rewardable->configuration.presetVariable("secondarySkill", "gainedSkill", variable);
} }
return object; return object;
} }
@ -1458,7 +1475,7 @@ CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr<const ObjectTemplat
return readSeerHut(mapPosition, objectInstanceID); return readSeerHut(mapPosition, objectInstanceID);
case Obj::WITCH_HUT: case Obj::WITCH_HUT:
return readWitchHut(); return readWitchHut(mapPosition, objectTemplate);
case Obj::SCHOLAR: case Obj::SCHOLAR:
return readScholar(); return readScholar();
@ -1717,7 +1734,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
{ {
if(!object->secSkills.empty()) if(!object->secSkills.empty())
{ {
if(object->secSkills[0].first != SecondarySkill::DEFAULT) if(object->secSkills[0].first != SecondarySkill::NONE)
logGlobal->debug("Map '%s': Hero %s subID=%d has set secondary skills twice (in map properties and on adventure map instance). Using the latter set...", mapName, object->getNameTextID(), object->subID); logGlobal->debug("Map '%s': Hero %s subID=%d has set secondary skills twice (in map properties and on adventure map instance). Using the latter set...", mapName, object->getNameTextID(), object->subID);
object->secSkills.clear(); object->secSkills.clear();
} }

View File

@ -165,7 +165,7 @@ private:
CGObjectInstance * readSeerHut(const int3 & initialPos, const ObjectInstanceID & idToBeGiven); CGObjectInstance * readSeerHut(const int3 & initialPos, const ObjectInstanceID & idToBeGiven);
CGObjectInstance * readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); CGObjectInstance * readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
CGObjectInstance * readSign(const int3 & position); CGObjectInstance * readSign(const int3 & position);
CGObjectInstance * readWitchHut(); CGObjectInstance * readWitchHut(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);
CGObjectInstance * readScholar(); CGObjectInstance * readScholar();
CGObjectInstance * readGarrison(const int3 & mapPosition); CGObjectInstance * readGarrison(const int3 & mapPosition);
CGObjectInstance * readArtifact(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl); CGObjectInstance * readArtifact(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);

View File

@ -147,7 +147,6 @@ void registerTypesMapObjectTypes(Serializer &s)
REGISTER_GENERIC_HANDLER(CGWhirlpool); REGISTER_GENERIC_HANDLER(CGWhirlpool);
REGISTER_GENERIC_HANDLER(CGTownInstance); REGISTER_GENERIC_HANDLER(CGTownInstance);
REGISTER_GENERIC_HANDLER(CGUniversity); REGISTER_GENERIC_HANDLER(CGUniversity);
REGISTER_GENERIC_HANDLER(CGWitchHut);
REGISTER_GENERIC_HANDLER(HillFort); REGISTER_GENERIC_HANDLER(HillFort);
#undef REGISTER_GENERIC_HANDLER #undef REGISTER_GENERIC_HANDLER
@ -177,7 +176,6 @@ void registerTypesMapObjects2(Serializer &s)
s.template registerType<CGObjectInstance, CRewardableObject>(); s.template registerType<CGObjectInstance, CRewardableObject>();
s.template registerType<CGObjectInstance, CTeamVisited>(); s.template registerType<CGObjectInstance, CTeamVisited>();
s.template registerType<CTeamVisited, CGWitchHut>();
s.template registerType<CTeamVisited, CGShrine>(); s.template registerType<CTeamVisited, CGShrine>();
s.template registerType<CTeamVisited, CCartographer>(); s.template registerType<CTeamVisited, CCartographer>();
s.template registerType<CTeamVisited, CGObelisk>(); s.template registerType<CTeamVisited, CGObelisk>();

View File

@ -24,6 +24,38 @@ ui16 Rewardable::Configuration::getResetDuration() const
return resetParameters.period; return resetParameters.period;
} }
std::optional<int> Rewardable::Configuration::getVariable(const std::string & category, const std::string & name) const
{
std::string variableID = category + '@' + name;
if (variables.values.count(variableID))
return variables.values.at(variableID);
return std::nullopt;
}
JsonNode Rewardable::Configuration::getPresetVariable(const std::string & category, const std::string & name) const
{
std::string variableID = category + '@' + name;
if (variables.values.count(variableID))
return variables.preset.at(variableID);
else
return JsonNode();
}
void Rewardable::Configuration::presetVariable(const std::string & category, const std::string & name, const JsonNode & value)
{
std::string variableID = category + '@' + name;
variables.preset[variableID] = value;
}
void Rewardable::Configuration::initVariable(const std::string & category, const std::string & name, int value)
{
std::string variableID = category + '@' + name;
variables.values[variableID] = value;
}
void Rewardable::ResetInfo::serializeJson(JsonSerializeFormat & handler) void Rewardable::ResetInfo::serializeJson(JsonSerializeFormat & handler)
{ {
handler.serializeInt("period", period); handler.serializeInt("period", period);
@ -39,6 +71,11 @@ void Rewardable::VisitInfo::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("visitType", visitType); handler.serializeInt("visitType", visitType);
} }
void Rewardable::Variables::serializeJson(JsonSerializeFormat & handler)
{
// TODO
}
void Rewardable::Configuration::serializeJson(JsonSerializeFormat & handler) void Rewardable::Configuration::serializeJson(JsonSerializeFormat & handler)
{ {
handler.serializeStruct("onSelect", onSelect); handler.serializeStruct("onSelect", onSelect);

View File

@ -62,7 +62,6 @@ struct DLL_LINKAGE ResetInfo
/// if true - reset list of visitors (heroes & players) on reset /// if true - reset list of visitors (heroes & players) on reset
bool visitors; bool visitors;
/// if true - re-randomize rewards on a new week /// if true - re-randomize rewards on a new week
bool rewards; bool rewards;
@ -98,6 +97,23 @@ struct DLL_LINKAGE VisitInfo
} }
}; };
struct DLL_LINKAGE Variables
{
/// List of variables used by this object in their current values
std::map<std::string, int> values;
/// List of per-instance preconfigured variables, e.g. from map
std::map<std::string, JsonNode> preset;
void serializeJson(JsonSerializeFormat & handler);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & values;
h & preset;
}
};
/// Base class that can handle granting rewards to visiting heroes. /// Base class that can handle granting rewards to visiting heroes.
struct DLL_LINKAGE Configuration struct DLL_LINKAGE Configuration
{ {
@ -116,6 +132,9 @@ struct DLL_LINKAGE Configuration
/// how and when should the object be reset /// how and when should the object be reset
Rewardable::ResetInfo resetParameters; Rewardable::ResetInfo resetParameters;
/// List of variables shoread between all limiters and rewards
Rewardable::Variables variables;
/// if true - player can refuse visiting an object (e.g. Tomb) /// if true - player can refuse visiting an object (e.g. Tomb)
bool canRefuse = false; bool canRefuse = false;
@ -125,6 +144,11 @@ struct DLL_LINKAGE Configuration
EVisitMode getVisitMode() const; EVisitMode getVisitMode() const;
ui16 getResetDuration() const; ui16 getResetDuration() const;
std::optional<int> getVariable(const std::string & category, const std::string & name) const;
JsonNode getPresetVariable(const std::string & category, const std::string & name) const;
void presetVariable(const std::string & category, const std::string & name, const JsonNode & value);
void initVariable(const std::string & category, const std::string & name, int value);
void serializeJson(JsonSerializeFormat & handler); void serializeJson(JsonSerializeFormat & handler);
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)

View File

@ -102,26 +102,23 @@ Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Conf
void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const
{ {
std::vector<SpellID> spells; auto const & variables = object.variables.values;
IObjectInterface::cb->getAllowedSpells(spells);
limiter.dayOfWeek = JsonRandom::loadValue(source["dayOfWeek"], rng, variables);
limiter.daysPassed = JsonRandom::loadValue(source["daysPassed"], rng, variables);
limiter.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng, variables);
limiter.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng, variables);
limiter.dayOfWeek = JsonRandom::loadValue(source["dayOfWeek"], rng); limiter.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, variables);
limiter.daysPassed = JsonRandom::loadValue(source["daysPassed"], rng); limiter.manaPoints = JsonRandom::loadValue(source["manaPoints"], rng, variables);
limiter.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng);
limiter.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng)
+ JsonRandom::loadValue(source["minLevel"], rng); // VCMI 1.1 compatibilty
limiter.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng); limiter.resources = JsonRandom::loadResources(source["resources"], rng, variables);
limiter.manaPoints = JsonRandom::loadValue(source["manaPoints"], rng);
limiter.resources = JsonRandom::loadResources(source["resources"], rng); limiter.primary = JsonRandom::loadPrimary(source["primary"], rng, variables);
limiter.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
limiter.primary = JsonRandom::loadPrimary(source["primary"], rng); limiter.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
limiter.secondary = JsonRandom::loadSecondary(source["secondary"], rng); limiter.spells = JsonRandom::loadSpells(source["spells"], rng, variables);
limiter.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng); limiter.creatures = JsonRandom::loadCreatures(source["creatures"], rng, variables);
limiter.spells = JsonRandom::loadSpells(source["spells"], rng, spells);
limiter.creatures = JsonRandom::loadCreatures(source["creatures"], rng);
limiter.players = JsonRandom::loadColors(source["colors"], rng); limiter.players = JsonRandom::loadColors(source["colors"], rng);
limiter.heroes = JsonRandom::loadHeroes(source["heroes"], rng); limiter.heroes = JsonRandom::loadHeroes(source["heroes"], rng);
@ -134,36 +131,32 @@ void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRan
void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Reward & reward, const JsonNode & source) const void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Reward & reward, const JsonNode & source) const
{ {
reward.resources = JsonRandom::loadResources(source["resources"], rng); auto const & variables = object.variables.values;
reward.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng) reward.resources = JsonRandom::loadResources(source["resources"], rng, variables);
+ JsonRandom::loadValue(source["gainedExp"], rng); // VCMI 1.1 compatibilty
reward.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng) reward.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng, variables);
+ JsonRandom::loadValue(source["gainedLevels"], rng); // VCMI 1.1 compatibilty reward.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng, variables);
reward.manaDiff = JsonRandom::loadValue(source["manaPoints"], rng); reward.manaDiff = JsonRandom::loadValue(source["manaPoints"], rng, variables);
reward.manaOverflowFactor = JsonRandom::loadValue(source["manaOverflowFactor"], rng); reward.manaOverflowFactor = JsonRandom::loadValue(source["manaOverflowFactor"], rng, variables);
reward.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, -1); reward.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, variables, -1);
reward.movePoints = JsonRandom::loadValue(source["movePoints"], rng); reward.movePoints = JsonRandom::loadValue(source["movePoints"], rng, variables);
reward.movePercentage = JsonRandom::loadValue(source["movePercentage"], rng, -1); reward.movePercentage = JsonRandom::loadValue(source["movePercentage"], rng, variables, -1);
reward.removeObject = source["removeObject"].Bool(); reward.removeObject = source["removeObject"].Bool();
reward.bonuses = JsonRandom::loadBonuses(source["bonuses"]); reward.bonuses = JsonRandom::loadBonuses(source["bonuses"]);
reward.primary = JsonRandom::loadPrimary(source["primary"], rng); reward.primary = JsonRandom::loadPrimary(source["primary"], rng, variables);
reward.secondary = JsonRandom::loadSecondary(source["secondary"], rng); reward.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
std::vector<SpellID> spells; reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
IObjectInterface::cb->getAllowedSpells(spells); reward.spells = JsonRandom::loadSpells(source["spells"], rng, variables);
reward.creatures = JsonRandom::loadCreatures(source["creatures"], rng, variables);
reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng);
reward.spells = JsonRandom::loadSpells(source["spells"], rng, spells);
reward.creatures = JsonRandom::loadCreatures(source["creatures"], rng);
if(!source["spellCast"].isNull() && source["spellCast"].isStruct()) if(!source["spellCast"].isNull() && source["spellCast"].isStruct())
{ {
reward.spellCast.first = JsonRandom::loadSpell(source["spellCast"]["spell"], rng); reward.spellCast.first = JsonRandom::loadSpell(source["spellCast"]["spell"], rng, variables);
reward.spellCast.second = source["spellCast"]["schoolLevel"].Integer(); reward.spellCast.second = source["spellCast"]["schoolLevel"].Integer();
} }
@ -185,11 +178,48 @@ void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, CR
resetParameters.rewards = source["rewards"].Bool(); resetParameters.rewards = source["rewards"].Bool();
} }
void Rewardable::Info::configureVariables(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const
{
for(const auto & category : source.Struct())
{
for(const auto & entry : category.second.Struct())
{
JsonNode preset = object.getPresetVariable(category.first, entry.first);
int32_t value = -1;
if (category.first == "number")
value = JsonRandom::loadValue(entry.second, rng, object.variables.values);
if (category.first == "artifact")
value = JsonRandom::loadArtifact(entry.second, rng, object.variables.values).getNum();
if (category.first == "spell")
value = JsonRandom::loadSpell(entry.second, rng, object.variables.values).getNum();
// TODO
// if (category.first == "creature")
// value = JsonRandom::loadCreature(entry.second, rng, object.variables.values).type->getId();
// TODO
// if (category.first == "resource")
// value = JsonRandom::loadResource(entry.second, rng, object.variables.values).getNum();
// TODO
// if (category.first == "primarySkill")
// value = JsonRandom::loadCreature(entry.second, rng, object.variables.values).getNum();
if (category.first == "secondarySkill")
value = JsonRandom::loadSecondary(entry.second, rng, object.variables.values).getNum();
object.initVariable(category.first, entry.first, value);
}
}
}
void Rewardable::Info::configureRewards( void Rewardable::Info::configureRewards(
Rewardable::Configuration & object, Rewardable::Configuration & object,
CRandomGenerator & rng, const CRandomGenerator & rng, const
JsonNode & source, JsonNode & source,
std::map<si32, si32> & thrownDice,
Rewardable::EEventType event, Rewardable::EEventType event,
const std::string & modeName) const const std::string & modeName) const
{ {
@ -200,21 +230,27 @@ void Rewardable::Info::configureRewards(
if (!reward["appearChance"].isNull()) if (!reward["appearChance"].isNull())
{ {
JsonNode chance = reward["appearChance"]; JsonNode chance = reward["appearChance"];
si32 diceID = static_cast<si32>(chance["dice"].Float()); std::string diceID = std::to_string(chance["dice"].Integer());
if (thrownDice.count(diceID) == 0) auto diceValue = object.getVariable("dice", diceID);
thrownDice[diceID] = rng.getIntRange(0, 99)();
if (!diceValue.has_value())
{
object.initVariable("dice", diceID, rng.getIntRange(0, 99)());
diceValue = object.getVariable("dice", diceID);
}
assert(diceValue.has_value());
if (!chance["min"].isNull()) if (!chance["min"].isNull())
{ {
int min = static_cast<int>(chance["min"].Float()); int min = static_cast<int>(chance["min"].Float());
if (min > thrownDice[diceID]) if (min > *diceValue)
continue; continue;
} }
if (!chance["max"].isNull()) if (!chance["max"].isNull())
{ {
int max = static_cast<int>(chance["max"].Float()); int max = static_cast<int>(chance["max"].Float());
if (max <= thrownDice[diceID]) if (max <= *diceValue)
continue; continue;
} }
} }
@ -240,11 +276,11 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, CRand
{ {
object.info.clear(); object.info.clear();
std::map<si32, si32> thrownDice; configureVariables(object, rng, parameters["variables"]);
configureRewards(object, rng, parameters["rewards"], thrownDice, Rewardable::EEventType::EVENT_FIRST_VISIT, "rewards"); configureRewards(object, rng, parameters["rewards"], Rewardable::EEventType::EVENT_FIRST_VISIT, "rewards");
configureRewards(object, rng, parameters["onVisited"], thrownDice, Rewardable::EEventType::EVENT_ALREADY_VISITED, "onVisited"); configureRewards(object, rng, parameters["onVisited"], Rewardable::EEventType::EVENT_ALREADY_VISITED, "onVisited");
configureRewards(object, rng, parameters["onEmpty"], thrownDice, Rewardable::EEventType::EVENT_NOT_AVAILABLE, "onEmpty"); configureRewards(object, rng, parameters["onEmpty"], Rewardable::EEventType::EVENT_NOT_AVAILABLE, "onEmpty");
object.onSelect = loadMessage(parameters["onSelectMessage"], TextIdentifier(objectTextID, "onSelect")); object.onSelect = loadMessage(parameters["onSelectMessage"], TextIdentifier(objectTextID, "onSelect"));

View File

@ -32,7 +32,8 @@ class DLL_LINKAGE Info : public IObjectInfo
JsonNode parameters; JsonNode parameters;
std::string objectTextID; std::string objectTextID;
void configureRewards(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source, std::map<si32, si32> & thrownDice, Rewardable::EEventType mode, const std::string & textPrefix) const; void configureVariables(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const;
void configureRewards(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source, Rewardable::EEventType mode, const std::string & textPrefix) const;
void configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const; void configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const;
Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const; Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const;

View File

@ -10,8 +10,6 @@
#pragma once #pragma once
#include "../CCreatureSet.h"
#include "../ResourceSet.h"
#include "../spells/ExternalCaster.h" #include "../spells/ExternalCaster.h"
#include "Configuration.h" #include "Configuration.h"