mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Implemented basic version of configurable Witch Hut
This commit is contained in:
		| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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>; | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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 | ||||
|     }, | ||||
|     { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
| 		{ | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -35,8 +35,7 @@ public: | ||||
| 	{ | ||||
| 		AObjectTypeHandler::serialize(h, version); | ||||
|  | ||||
| 		if (version >= 816) | ||||
| 			h & objectInfo; | ||||
| 		h & objectInfo; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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()); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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)); | ||||
| 		} | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
| 		{ | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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(); | ||||
| 		} | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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>(); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
| 	 | ||||
|   | ||||
| @@ -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")); | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -10,8 +10,6 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../CCreatureSet.h" | ||||
| #include "../ResourceSet.h" | ||||
| #include "../spells/ExternalCaster.h" | ||||
| #include "Configuration.h" | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user