1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-05-13 22:06:58 +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))
return role == HeroRole::SCOUT ? 2 : 0;
auto skill = SecondarySkill(hut->ability);
if(hero->getSecSkillLevel(skill) != SecSkillLevel::NONE
|| hero->secSkills.size() >= GameConstants::SKILL_PER_HERO)
return 0;
@ -575,7 +578,7 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
case Obj::LIBRARY_OF_ENLIGHTENMENT:
return 8;
case Obj::WITCH_HUT:
return evaluateWitchHutSkillScore(dynamic_cast<const CGWitchHut *>(target), hero, role);
return evaluateWitchHutSkillScore(target, hero, role);
case Obj::PANDORAS_BOX:
//Can contains experience, spells, or skills (only on custom maps)
return 2.5f;

View File

@ -18,8 +18,6 @@
VCMI_LIB_NAMESPACE_BEGIN
class CGWitchHut;
VCMI_LIB_NAMESPACE_END
namespace NKAI
@ -43,7 +41,7 @@ public:
float getResourceRequirementStrength(int resType) const;
float getStrategicalValue(const CGObjectInstance * target) 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;
int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) 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 income = estimateIncome();
GameResID resourceType = EGameResID::INVALID;
GameResID resourceType = EGameResID::NONE;
TResource amountToCollect = 0;
using resPair = std::pair<GameResID, TResource>;
@ -129,7 +129,7 @@ Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o
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
using timePair = std::pair<GameResID, float>;

View File

@ -52,6 +52,7 @@
"config/objects/moddables.json",
"config/objects/creatureBanks.json",
"config/objects/dwellings.json",
"config/objects/rewardableGeneric.json",
"config/objects/rewardableOncePerWeek.json",
"config/objects/rewardablePickable.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" : {
"index" :215,
"handler" : "questGuard",

View File

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

View File

@ -48,7 +48,7 @@ private:
std::string identifier;
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;
enum class Obligatory : ui8

View File

@ -32,38 +32,71 @@ VCMI_LIB_NAMESPACE_BEGIN
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())
return defaultValue;
if(value.isNumber())
return static_cast<si32>(value.Float());
if (value.isString())
return loadVariable("number", value.String(), variables, defaultValue);
if(value.isVector())
{
const auto & vector = value.Vector();
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["amount"].isNull())
return static_cast<si32>(loadValue(value["amount"], rng, defaultValue));
si32 min = static_cast<si32>(loadValue(value["min"], rng, 0));
si32 max = static_cast<si32>(loadValue(value["max"], rng, 0));
return static_cast<si32>(loadValue(value["amount"], rng, variables, defaultValue));
si32 min = static_cast<si32>(loadValue(value["min"], rng, variables, 0));
si32 max = static_cast<si32>(loadValue(value["max"], rng, variables, 0));
return rng.getIntRange(min, max)();
}
return defaultValue;
}
template<typename IdentifierType>
IdentifierType decodeKey(const JsonNode & value)
IdentifierType decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
{
return IdentifierType(*VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value));
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));
else
return loadVariable(IdentifierType::entityType(), value.String(), variables, IdentifierType::NONE);
}
template<>
PrimarySkill decodeKey(const JsonNode & value)
PrimarySkill decodeKey(const JsonNode & value, const Variables & variables)
{
return PrimarySkill(*VLC->identifiers()->getIdentifier("primarySkill", value));
}
@ -143,7 +176,7 @@ namespace JsonRandom
if (!value["level"].isNull())
{
int32_t spellLevel = value["level"].Float();
int32_t spellLevel = value["level"].Integer();
vstd::erase_if(result, [=](const SpellID & spell)
{
@ -164,17 +197,17 @@ namespace JsonRandom
}
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())
return { decodeKey<IdentifierType>(value) };
return { decodeKey<IdentifierType>(value, variables) };
assert(value.isStruct());
if(value.isStruct())
{
if(!value["type"].isNull())
return filterKeys(value["type"], valuesSet);
return filterKeys(value["type"], valuesSet, variables);
std::set<IdentifierType> filteredTypes = filterKeysTyped(value, valuesSet);
@ -183,7 +216,7 @@ namespace JsonRandom
std::set<IdentifierType> filteredAnyOf;
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());
}
@ -197,7 +230,7 @@ namespace JsonRandom
{
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 )
filteredTypes.erase(bannedEntry);
}
@ -208,25 +241,25 @@ namespace JsonRandom
return valuesSet;
}
TResources loadResources(const JsonNode & value, CRandomGenerator & rng)
TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{
TResources ret;
if (value.isVector())
{
for (const auto & entry : value.Vector())
ret += loadResource(entry, rng);
ret += loadResource(entry, rng, variables);
return ret;
}
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;
}
TResources loadResource(const JsonNode & value, CRandomGenerator & rng)
TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{
std::set<GameResID> defaultResources{
GameResID::WOOD,
@ -238,9 +271,9 @@ namespace JsonRandom
GameResID::GOLD
};
std::set<GameResID> potentialPicks = filterKeys(value, defaultResources);
std::set<GameResID> potentialPicks = filterKeys(value, defaultResources, variables);
GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
si32 resourceAmount = loadValue(value, rng, 0);
si32 resourceAmount = loadValue(value, rng, variables, 0);
TResources ret;
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;
if(value.isStruct())
{
for(const auto & name : NPrimarySkill::names)
{
ret.push_back(loadValue(value[name], rng));
ret.push_back(loadValue(value[name], rng, variables));
}
}
if(value.isVector())
@ -271,25 +304,36 @@ namespace JsonRandom
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);
defaultSkills.erase(skillID);
ret[static_cast<int>(skillID)] += loadValue(element, rng);
ret[static_cast<int>(skillID)] += loadValue(element, rng, variables);
}
}
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;
if(value.isStruct())
{
for(const auto & pair : value.Struct())
{
SecondarySkill id(VLC->identifiers()->getIdentifier(pair.second.meta, "skill", pair.first).value());
ret[id] = loadValue(pair.second, rng);
SecondarySkill id = decodeKey<SecondarySkill>(pair.second.meta, pair.first, variables);
ret[id] = loadValue(pair.second, rng, variables);
}
}
if(value.isVector())
@ -301,45 +345,45 @@ namespace JsonRandom
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);
defaultSkills.erase(skillID); //avoid dupicates
ret[skillID] = loadValue(element, rng);
ret[skillID] = loadValue(element, rng, variables);
}
}
return ret;
}
ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng)
ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{
std::set<ArtifactID> allowedArts;
for (auto const * artifact : VLC->arth->allowedArtifacts)
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);
}
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;
for (const JsonNode & entry : value.Vector())
{
ret.push_back(loadArtifact(entry, rng));
ret.push_back(loadArtifact(entry, rng, variables));
}
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;
for(const auto & spell : VLC->spellh->objects)
if (IObjectInterface::cb->isAllowed(0, spell->getIndex()))
defaultSpells.insert(spell->getId());
std::set<SpellID> potentialPicks = filterKeys(value, defaultSpells);
std::set<SpellID> potentialPicks = filterKeys(value, defaultSpells, variables);
if (potentialPicks.empty())
{
@ -349,12 +393,12 @@ namespace JsonRandom
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;
for (const JsonNode & entry : value.Vector())
{
ret.push_back(loadSpell(entry, rng, spells));
ret.push_back(loadSpell(entry, rng, variables));
}
return ret;
}
@ -399,7 +443,7 @@ namespace JsonRandom
return ret;
}
CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng)
CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{
CStackBasicDescriptor stack;
@ -408,7 +452,7 @@ namespace JsonRandom
if (!creature->special)
defaultCreatures.insert(creature->getId());
std::set<CreatureID> potentialPicks = filterKeys(value, defaultCreatures);
std::set<CreatureID> potentialPicks = filterKeys(value, defaultCreatures, variables);
CreatureID pickedCreature;
if (!potentialPicks.empty())
@ -417,7 +461,7 @@ namespace JsonRandom
logMod->warn("Failed to select suitable random creature!");
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 (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade
@ -428,17 +472,17 @@ namespace JsonRandom
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;
for (const JsonNode & node : value.Vector())
{
ret.push_back(loadCreature(node, rng));
ret.push_back(loadCreature(node, rng, variables));
}
return ret;
}
std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value)
std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables)
{
std::vector<RandomStackInfo> ret;
for (const JsonNode & node : value.Vector())
@ -464,13 +508,6 @@ namespace JsonRandom
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> ret;

View File

@ -24,6 +24,8 @@ class CStackBasicDescriptor;
namespace JsonRandom
{
using Variables = std::map<std::string, int>;
struct DLL_LINKAGE RandomStackInfo
{
std::vector<const CCreature *> allowedCreatures;
@ -31,29 +33,29 @@ namespace JsonRandom
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 loadResource(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::map<SecondarySkill, si32> loadSecondary(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, const Variables & variables);
DLL_LINKAGE std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
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 std::vector<ArtifactID> loadArtifacts(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, const Variables & variables);
DLL_LINKAGE SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> spells = {});
DLL_LINKAGE std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const 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 Variables & variables);
DLL_LINKAGE CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value);
DLL_LINKAGE CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
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<HeroTypeID> loadHeroes(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<Component> loadComponents(const JsonNode & value);
}
VCMI_LIB_NAMESPACE_END

View File

@ -308,8 +308,7 @@ class SecondarySkillBase : public IdentifierBase
public:
enum Type : int32_t
{
WRONG = -2,
DEFAULT = -1,
NONE = -1,
PATHFINDING = 0,
ARCHERY,
LOGISTICS,
@ -938,7 +937,7 @@ public:
COUNT,
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 bc;
JsonRandom::Variables emptyVariables;
bc.chance = static_cast<ui32>(level["chance"].Float());
bc.guards = JsonRandom::loadCreatures(level["guards"], rng);
std::vector<SpellID> spells;
IObjectInterface::cb->getAllowedSpells(spells);
bc.guards = JsonRandom::loadCreatures(level["guards"], rng, emptyVariables);
bc.resources = ResourceSet(level["reward"]["resources"]);
bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng);
bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng);
bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells);
bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng, emptyVariables);
bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng, emptyVariables);
bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, emptyVariables);
return bc;
}
@ -105,10 +103,12 @@ static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature *
IObjectInfo::CArmyStructure CBankInfo::minGuards() const
{
JsonRandom::Variables emptyVariables;
std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config)
{
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"], emptyVariables);
IObjectInfo::CArmyStructure army;
for(auto & stack : stacks)
{
@ -126,10 +126,12 @@ IObjectInfo::CArmyStructure CBankInfo::minGuards() const
IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
{
JsonRandom::Variables emptyVariables;
std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config)
{
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"], emptyVariables);
IObjectInfo::CArmyStructure army;
for(auto & stack : stacks)
{
@ -147,12 +149,13 @@ IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
TPossibleGuards CBankInfo::getPossibleGuards() const
{
JsonRandom::Variables emptyVariables;
TPossibleGuards out;
for(const JsonNode & configEntry : config)
{
const JsonNode & guardsInfo = configEntry["guards"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
auto stacks = JsonRandom::evaluateCreatures(guardsInfo, emptyVariables);
IObjectInfo::CArmyStructure army;
@ -187,12 +190,13 @@ std::vector<PossibleReward<TResources>> CBankInfo::getPossibleResourcesReward()
std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const
{
JsonRandom::Variables emptyVariables;
std::vector<PossibleReward<CStackBasicDescriptor>> aproximateReward;
for(const JsonNode & configEntry : config)
{
const JsonNode & guardsInfo = configEntry["reward"]["creatures"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
auto stacks = JsonRandom::evaluateCreatures(guardsInfo, emptyVariables);
for(auto stack : stacks)
{

View File

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

View File

@ -35,8 +35,7 @@ public:
{
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
{
JsonRandom::Variables emptyVariables;
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());
}
}

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)
{
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));
}

View File

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

View File

@ -253,7 +253,7 @@ CGHeroInstance::CGHeroInstance():
{
setNodeType(HERO);
ID = Obj::HERO;
secSkills.emplace_back(SecondarySkill::DEFAULT, -1);
secSkills.emplace_back(SecondarySkill::NONE, -1);
blockVisit = true;
}
@ -315,7 +315,7 @@ void CGHeroInstance::initHero(CRandomGenerator & rand)
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;
if (gender == EHeroGender::DEFAULT)
@ -1580,7 +1580,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
bool normalSkills = false;
for(const auto & p : secSkills)
{
if(p.first == SecondarySkill(SecondarySkill::DEFAULT))
if(p.first == SecondarySkill(SecondarySkill::NONE))
defaultSkills = true;
else
normalSkills = true;
@ -1618,7 +1618,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
secSkills.clear();
if(secondarySkills.getType() == JsonNode::JsonType::DATA_NULL)
{
secSkills.emplace_back(SecondarySkill::DEFAULT, -1);
secSkills.emplace_back(SecondarySkill::NONE, -1);
}
else
{

View File

@ -90,7 +90,6 @@ public:
// POSSIBLE
// class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
// class DLL_LINKAGE CGWitchHut : public CPlayersVisited
// class DLL_LINKAGE CGScholar : public CGObjectInstance
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
{
InfoWindow iw;

View File

@ -57,26 +57,6 @@ protected:
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
{
public:

View File

@ -1139,23 +1139,40 @@ CGObjectInstance * CMapLoaderH3M::readSign(const int3 & mapPosition)
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
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();
for(int skillID = 0; skillID < VLC->skillh->size(); ++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;
}
@ -1458,7 +1475,7 @@ CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr<const ObjectTemplat
return readSeerHut(mapPosition, objectInstanceID);
case Obj::WITCH_HUT:
return readWitchHut();
return readWitchHut(mapPosition, objectTemplate);
case Obj::SCHOLAR:
return readScholar();
@ -1717,7 +1734,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
{
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);
object->secSkills.clear();
}

View File

@ -165,7 +165,7 @@ private:
CGObjectInstance * readSeerHut(const int3 & initialPos, const ObjectInstanceID & idToBeGiven);
CGObjectInstance * readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
CGObjectInstance * readSign(const int3 & position);
CGObjectInstance * readWitchHut();
CGObjectInstance * readWitchHut(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);
CGObjectInstance * readScholar();
CGObjectInstance * readGarrison(const int3 & mapPosition);
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(CGTownInstance);
REGISTER_GENERIC_HANDLER(CGUniversity);
REGISTER_GENERIC_HANDLER(CGWitchHut);
REGISTER_GENERIC_HANDLER(HillFort);
#undef REGISTER_GENERIC_HANDLER
@ -177,7 +176,6 @@ void registerTypesMapObjects2(Serializer &s)
s.template registerType<CGObjectInstance, CRewardableObject>();
s.template registerType<CGObjectInstance, CTeamVisited>();
s.template registerType<CTeamVisited, CGWitchHut>();
s.template registerType<CTeamVisited, CGShrine>();
s.template registerType<CTeamVisited, CCartographer>();
s.template registerType<CTeamVisited, CGObelisk>();

View File

@ -24,6 +24,38 @@ ui16 Rewardable::Configuration::getResetDuration() const
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)
{
handler.serializeInt("period", period);
@ -39,6 +71,11 @@ void Rewardable::VisitInfo::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("visitType", visitType);
}
void Rewardable::Variables::serializeJson(JsonSerializeFormat & handler)
{
// TODO
}
void Rewardable::Configuration::serializeJson(JsonSerializeFormat & handler)
{
handler.serializeStruct("onSelect", onSelect);

View File

@ -62,7 +62,6 @@ struct DLL_LINKAGE ResetInfo
/// if true - reset list of visitors (heroes & players) on reset
bool visitors;
/// if true - re-randomize rewards on a new week
bool rewards;
@ -86,7 +85,7 @@ struct DLL_LINKAGE VisitInfo
/// Event to which this reward is assigned
EEventType visitType;
void serializeJson(JsonSerializeFormat & handler);
template <typename Handler> void serialize(Handler &h, const int version)
@ -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.
struct DLL_LINKAGE Configuration
{
@ -116,6 +132,9 @@ struct DLL_LINKAGE Configuration
/// how and when should the object be reset
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)
bool canRefuse = false;
@ -124,6 +143,11 @@ struct DLL_LINKAGE Configuration
EVisitMode getVisitMode() 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);

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
{
std::vector<SpellID> spells;
IObjectInterface::cb->getAllowedSpells(spells);
auto const & variables = object.variables.values;
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.daysPassed = JsonRandom::loadValue(source["daysPassed"], rng);
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, variables);
limiter.manaPoints = JsonRandom::loadValue(source["manaPoints"], rng, variables);
limiter.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng);
limiter.manaPoints = JsonRandom::loadValue(source["manaPoints"], rng);
limiter.resources = JsonRandom::loadResources(source["resources"], rng, variables);
limiter.resources = JsonRandom::loadResources(source["resources"], rng);
limiter.primary = JsonRandom::loadPrimary(source["primary"], rng);
limiter.secondary = JsonRandom::loadSecondary(source["secondary"], rng);
limiter.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng);
limiter.spells = JsonRandom::loadSpells(source["spells"], rng, spells);
limiter.creatures = JsonRandom::loadCreatures(source["creatures"], rng);
limiter.primary = JsonRandom::loadPrimary(source["primary"], rng, variables);
limiter.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
limiter.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
limiter.spells = JsonRandom::loadSpells(source["spells"], rng, variables);
limiter.creatures = JsonRandom::loadCreatures(source["creatures"], rng, variables);
limiter.players = JsonRandom::loadColors(source["colors"], 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
{
reward.resources = JsonRandom::loadResources(source["resources"], rng);
auto const & variables = object.variables.values;
reward.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng)
+ JsonRandom::loadValue(source["gainedExp"], rng); // VCMI 1.1 compatibilty
reward.resources = JsonRandom::loadResources(source["resources"], rng, variables);
reward.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng)
+ JsonRandom::loadValue(source["gainedLevels"], rng); // VCMI 1.1 compatibilty
reward.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng, variables);
reward.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng, variables);
reward.manaDiff = JsonRandom::loadValue(source["manaPoints"], rng);
reward.manaOverflowFactor = JsonRandom::loadValue(source["manaOverflowFactor"], rng);
reward.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, -1);
reward.manaDiff = JsonRandom::loadValue(source["manaPoints"], rng, variables);
reward.manaOverflowFactor = JsonRandom::loadValue(source["manaOverflowFactor"], rng, variables);
reward.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, variables, -1);
reward.movePoints = JsonRandom::loadValue(source["movePoints"], rng);
reward.movePercentage = JsonRandom::loadValue(source["movePercentage"], rng, -1);
reward.movePoints = JsonRandom::loadValue(source["movePoints"], rng, variables);
reward.movePercentage = JsonRandom::loadValue(source["movePercentage"], rng, variables, -1);
reward.removeObject = source["removeObject"].Bool();
reward.bonuses = JsonRandom::loadBonuses(source["bonuses"]);
reward.primary = JsonRandom::loadPrimary(source["primary"], rng);
reward.secondary = JsonRandom::loadSecondary(source["secondary"], rng);
reward.primary = JsonRandom::loadPrimary(source["primary"], rng, variables);
reward.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
std::vector<SpellID> spells;
IObjectInterface::cb->getAllowedSpells(spells);
reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng);
reward.spells = JsonRandom::loadSpells(source["spells"], rng, spells);
reward.creatures = JsonRandom::loadCreatures(source["creatures"], rng);
reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
reward.spells = JsonRandom::loadSpells(source["spells"], rng, variables);
reward.creatures = JsonRandom::loadCreatures(source["creatures"], rng, variables);
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();
}
@ -185,11 +178,48 @@ void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, CR
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(
Rewardable::Configuration & object,
CRandomGenerator & rng, const
JsonNode & source,
std::map<si32, si32> & thrownDice,
Rewardable::EEventType event,
const std::string & modeName) const
{
@ -200,21 +230,27 @@ void Rewardable::Info::configureRewards(
if (!reward["appearChance"].isNull())
{
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)
thrownDice[diceID] = rng.getIntRange(0, 99)();
auto diceValue = object.getVariable("dice", diceID);
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())
{
int min = static_cast<int>(chance["min"].Float());
if (min > thrownDice[diceID])
if (min > *diceValue)
continue;
}
if (!chance["max"].isNull())
{
int max = static_cast<int>(chance["max"].Float());
if (max <= thrownDice[diceID])
if (max <= *diceValue)
continue;
}
}
@ -240,11 +276,11 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, CRand
{
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["onVisited"], thrownDice, Rewardable::EEventType::EVENT_ALREADY_VISITED, "onVisited");
configureRewards(object, rng, parameters["onEmpty"], thrownDice, Rewardable::EEventType::EVENT_NOT_AVAILABLE, "onEmpty");
configureRewards(object, rng, parameters["rewards"], Rewardable::EEventType::EVENT_FIRST_VISIT, "rewards");
configureRewards(object, rng, parameters["onVisited"], Rewardable::EEventType::EVENT_ALREADY_VISITED, "onVisited");
configureRewards(object, rng, parameters["onEmpty"], Rewardable::EEventType::EVENT_NOT_AVAILABLE, "onEmpty");
object.onSelect = loadMessage(parameters["onSelectMessage"], TextIdentifier(objectTextID, "onSelect"));

View File

@ -32,7 +32,8 @@ class DLL_LINKAGE Info : public IObjectInfo
JsonNode parameters;
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;
Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, const JsonNode & source) const;

View File

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