mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Enabled new secondary skills to be created (#438)
* Universities, Scholars and Witch Huts may offer new skills * Moved encode/decodeSkill to CSkillHandler * Refactored CSkill interface and CSkill::LevelInfo image storage * Legacy game constants renamed to ORIGINAL_XXX_QUANTITY
This commit is contained in:
		
				
					committed by
					
						 ArseniyShestakov
						ArseniyShestakov
					
				
			
			
				
	
			
			
			
						parent
						
							b09a54fa9c
						
					
				
				
					commit
					6ddcb079a4
				
			| @@ -25,7 +25,7 @@ SPELLS: | ||||
|  | ||||
| MODS: | ||||
| * Improve support for WoG commander artifacts and skill descriptions | ||||
| * Added basic support for secondary skill modding | ||||
| * Added support for modding of original secondary skills and creation of new ones. | ||||
| * Map object sounds can now be configured via json | ||||
| * Added bonus updaters for hero specialties | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "../lib/CGeneralTextHandler.h" | ||||
| #include "../lib/CCreatureHandler.h" | ||||
| #include "CBitmapHandler.h" | ||||
| #include "../lib/CSkillHandler.h" | ||||
| #include "../lib/spells/CSpellHandler.h" | ||||
| #include "../lib/CGameState.h" | ||||
| #include "../lib/JsonNode.h" | ||||
| @@ -443,4 +444,16 @@ void Graphics::initializeImageLists() | ||||
| 		addImageListEntry(spell->id, "SPELLBON", spell->iconScenarioBonus); | ||||
| 		addImageListEntry(spell->id, "SPELLSCR", spell->iconScroll); | ||||
| 	} | ||||
|  | ||||
| 	for(const CSkill * skill : CGI->skillh->objects) | ||||
| 	{ | ||||
| 		for(int level = 1; level <= 3; level++) | ||||
| 		{ | ||||
| 			int frame = 2 + level + 3 * skill->id; | ||||
| 			const CSkill::LevelInfo & skillAtLevel = skill->at(level); | ||||
| 			addImageListEntry(frame, "SECSK32", skillAtLevel.iconSmall); | ||||
| 			addImageListEntry(frame, "SECSKILL", skillAtLevel.iconMedium); | ||||
| 			addImageListEntry(frame, "SECSK82", skillAtLevel.iconLarge); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,27 @@ | ||||
| 			"description" : "Set of bonuses provided by skill at given level", | ||||
| 			"required" : ["description", "effects"], | ||||
| 			"properties" : { | ||||
| 				"images" : { | ||||
| 					"type" : "object", | ||||
| 					"description" : "skill icons of varying size", | ||||
| 					"properties" : { | ||||
| 						"small" : { | ||||
| 							"type" : "string", | ||||
| 							"description" : "32x32 skill icon", | ||||
| 							"format" : "imageFile" | ||||
| 						}, | ||||
| 						"medium" : { | ||||
| 							"type" : "string", | ||||
| 							"description" : "44x44 skill icon", | ||||
| 							"format" : "imageFile" | ||||
| 						}, | ||||
| 						"large" : { | ||||
| 							"type" : "string", | ||||
| 							"description" : "82x93 skill icon", | ||||
| 							"format" : "imageFile" | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				"description" : { | ||||
| 					"type" : "string", | ||||
| 					"description" : "localizable description" | ||||
| @@ -39,6 +60,28 @@ | ||||
| 			"type": "string", | ||||
| 			"description": "localizable skill name" | ||||
| 		}, | ||||
| 		"gainChance" : { | ||||
| 			"description" : "Chance for the skill to be offered on level-up (heroClass may override)", | ||||
| 			"anyOf" : [ | ||||
| 				{ | ||||
| 					"type" : "number" | ||||
| 				}, | ||||
| 				{ | ||||
| 					"type" : "object", | ||||
| 					"required" : ["might", "magic"], | ||||
| 					"properties" : { | ||||
| 						"might" : { | ||||
| 							"type" : "number", | ||||
| 							"description" : "Chance for hero classes with might affinity" | ||||
| 						}, | ||||
| 						"magic" : { | ||||
| 							"type" : "number", | ||||
| 							"description" : "Chance for hero classes with magic affinity" | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			] | ||||
| 		}, | ||||
| 		"base" : { | ||||
| 			"type" : "object", | ||||
| 			"description" : "will be merged with all levels", | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| #include "CModHandler.h" | ||||
| #include "CTownHandler.h" | ||||
| #include "mapObjects/CObjectHandler.h" //for hero specialty | ||||
| #include "CSkillHandler.h" | ||||
| #include <math.h> | ||||
|  | ||||
| #include "mapObjects/CObjectClassesHandler.h" | ||||
| @@ -117,9 +118,15 @@ CHeroClass * CHeroClassHandler::loadFromJson(const JsonNode & node, const std::s | ||||
| 		heroClass->primarySkillHighLevel.push_back(node["highLevelChance"][pSkill].Float()); | ||||
| 	} | ||||
|  | ||||
| 	for(const std::string & secSkill : NSecondarySkill::names) | ||||
| 	for(auto skillPair : node["secondarySkills"].Struct()) | ||||
| 	{ | ||||
| 		heroClass->secSkillProbability.push_back(node["secondarySkills"][secSkill].Float()); | ||||
| 		int probability = skillPair.second.Integer(); | ||||
| 		VLC->modh->identifiers.requestIdentifier(skillPair.second.meta, "skill", skillPair.first, [heroClass, probability](si32 skillID) | ||||
| 		{ | ||||
| 			if(heroClass->secSkillProbability.size() <= skillID) | ||||
| 				heroClass->secSkillProbability.resize(skillID + 1, -1); // -1 = override with default later | ||||
| 			heroClass->secSkillProbability[skillID] = probability; | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	VLC->modh->identifiers.requestIdentifier ("creature", node["commander"], | ||||
| @@ -241,6 +248,17 @@ void CHeroClassHandler::afterLoadFinalization() | ||||
| 			float chance = heroClass->defaultTavernChance * faction->town->defaultTavernChance; | ||||
| 			heroClass->selectionProbability[faction->index] = static_cast<int>(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it | ||||
| 		} | ||||
| 		// set default probabilities for gaining secondary skills where not loaded previously | ||||
| 		heroClass->secSkillProbability.resize(VLC->skillh->size(), -1); | ||||
| 		for(int skillID = 0; skillID < VLC->skillh->size(); skillID++) | ||||
| 		{ | ||||
| 			if(heroClass->secSkillProbability[skillID] < 0) | ||||
| 			{ | ||||
| 				const CSkill * skill = (*VLC->skillh)[SecondarySkill(skillID)]; | ||||
| 				logMod->trace("%s: no probability for %s, using default", heroClass->identifier, skill->identifier); | ||||
| 				heroClass->secSkillProbability[skillID] = skill->gainChance[heroClass->affinity]; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for (CHeroClass * hc : heroClasses) | ||||
| @@ -277,11 +295,6 @@ CHeroHandler::CHeroHandler() | ||||
| { | ||||
| 	VLC->heroh = this; | ||||
|  | ||||
| 	for (int i = 0; i < GameConstants::SKILL_QUANTITY; ++i) | ||||
| 	{ | ||||
| 		VLC->modh->identifiers.registerObject("core", "skill", NSecondarySkill::names[i], i); | ||||
| 		VLC->modh->identifiers.registerObject("core", "secondarySkill", NSecondarySkill::names[i], i); | ||||
| 	} | ||||
| 	loadObstacles(); | ||||
| 	loadTerrains(); | ||||
| 	for (int i = 0; i < GameConstants::TERRAIN_TYPES; ++i) | ||||
| @@ -943,13 +956,6 @@ std::vector<bool> CHeroHandler::getDefaultAllowed() const | ||||
| 	return allowedHeroes; | ||||
| } | ||||
|  | ||||
| std::vector<bool> CHeroHandler::getDefaultAllowedAbilities() const | ||||
| { | ||||
| 	std::vector<bool> allowedAbilities; | ||||
| 	allowedAbilities.resize(GameConstants::SKILL_QUANTITY, true); | ||||
| 	return allowedAbilities; | ||||
| } | ||||
|  | ||||
| si32 CHeroHandler::decodeHero(const std::string & identifier) | ||||
| { | ||||
| 	auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", identifier); | ||||
| @@ -963,17 +969,3 @@ std::string CHeroHandler::encodeHero(const si32 index) | ||||
| { | ||||
| 	return VLC->heroh->heroes.at(index)->identifier; | ||||
| } | ||||
|  | ||||
| si32 CHeroHandler::decodeSkill(const std::string & identifier) | ||||
| { | ||||
| 	auto rawId = VLC->modh->identifiers.getIdentifier("core", "skill", identifier); | ||||
| 	if(rawId) | ||||
| 		return rawId.get(); | ||||
| 	else | ||||
| 		return -1; | ||||
| } | ||||
|  | ||||
| std::string CHeroHandler::encodeSkill(const si32 index) | ||||
| { | ||||
| 	return NSecondarySkill::names[index]; | ||||
| } | ||||
|   | ||||
| @@ -311,25 +311,12 @@ public: | ||||
|  | ||||
| 	std::vector<bool> getDefaultAllowed() const override; | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets a list of default allowed abilities. OH3 abilities/skills are all allowed by default. | ||||
| 	 * | ||||
| 	 * @return a list of allowed abilities, the index is the ability id | ||||
| 	 */ | ||||
| 	std::vector<bool> getDefaultAllowedAbilities() const; | ||||
|  | ||||
| 	///json serialization helper | ||||
| 	static si32 decodeHero(const std::string & identifier); | ||||
|  | ||||
| 	///json serialization helper | ||||
| 	static std::string encodeHero(const si32 index); | ||||
|  | ||||
| 	///json serialization helper | ||||
| 	static si32 decodeSkill(const std::string & identifier); | ||||
|  | ||||
| 	///json serialization helper | ||||
| 	static std::string encodeSkill(const si32 index); | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & classes; | ||||
|   | ||||
| @@ -240,7 +240,7 @@ std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdent | ||||
| 		{ | ||||
| 			// allow only available to all core mod or dependencies | ||||
| 			auto myDeps = VLC->modh->getModData(request.localScope).dependencies; | ||||
| 			if (request.remoteScope == "core" || myDeps.count(request.remoteScope)) | ||||
| 			if(request.remoteScope == "core" || request.remoteScope == request.localScope || myDeps.count(request.remoteScope)) | ||||
| 				allowedScopes.insert(request.remoteScope); | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -31,16 +31,10 @@ CSkill::LevelInfo::~LevelInfo() | ||||
| { | ||||
| } | ||||
|  | ||||
| CSkill::CSkill(SecondarySkill id) : id(id) | ||||
| CSkill::CSkill(SecondarySkill id, std::string identifier) | ||||
| 	: id(id), identifier(identifier) | ||||
| { | ||||
| 	if(id == SecondarySkill::DEFAULT) | ||||
| 		identifier = "default"; | ||||
| 	else | ||||
| 		identifier = NSecondarySkill::names[id]; | ||||
| 	// init levels | ||||
| 	LevelInfo emptyLevel; | ||||
| 	for(int level = 1; level < NSecondarySkill::levels.size(); level++) | ||||
| 		levels.push_back(emptyLevel); | ||||
| 	levels.resize(NSecondarySkill::levels.size() - 1); | ||||
| } | ||||
|  | ||||
| CSkill::~CSkill() | ||||
| @@ -56,19 +50,16 @@ void CSkill::addNewBonus(const std::shared_ptr<Bonus> & b, int level) | ||||
| 	levels[level-1].effects.push_back(b); | ||||
| } | ||||
|  | ||||
| void CSkill::setDescription(const std::string & desc, int level) | ||||
| const CSkill::LevelInfo & CSkill::at(int level) const | ||||
| { | ||||
| 	levels[level-1].description = desc; | ||||
| 	assert(1 <= level && level < NSecondarySkill::levels.size()); | ||||
| 	return levels[level - 1]; | ||||
| } | ||||
|  | ||||
| const std::vector<std::shared_ptr<Bonus>> & CSkill::getBonus(int level) const | ||||
| CSkill::LevelInfo & CSkill::at(int level) | ||||
| { | ||||
| 	return levels[level-1].effects; | ||||
| } | ||||
|  | ||||
| const std::string & CSkill::getDescription(int level) const | ||||
| { | ||||
| 	return levels[level-1].description; | ||||
| 	assert(1 <= level && level < NSecondarySkill::levels.size()); | ||||
| 	return levels[level - 1]; | ||||
| } | ||||
|  | ||||
| DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info) | ||||
| @@ -139,14 +130,15 @@ std::vector<JsonNode> CSkillHandler::loadLegacyData(size_t dataSize) | ||||
| 	return legacyData; | ||||
| } | ||||
|  | ||||
| const std::string CSkillHandler::getTypeName() const | ||||
| const std::vector<std::string> & CSkillHandler::getTypeNames() const | ||||
| { | ||||
| 	return "skill"; | ||||
| 	static const std::vector<std::string> typeNames = { "skill", "secondarySkill" }; | ||||
| 	return typeNames; | ||||
| } | ||||
|  | ||||
| const std::string & CSkillHandler::skillInfo(int skill, int level) const | ||||
| { | ||||
| 	return objects[skill]->getDescription(level); | ||||
| 	return objects[skill]->at(level).description; | ||||
| } | ||||
|  | ||||
| const std::string & CSkillHandler::skillName(int skill) const | ||||
| @@ -156,24 +148,22 @@ const std::string & CSkillHandler::skillName(int skill) const | ||||
|  | ||||
| CSkill * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) | ||||
| { | ||||
| 	CSkill * skill = nullptr; | ||||
|  | ||||
| 	for(int id = 0; id < GameConstants::SKILL_QUANTITY; id++) | ||||
| 	{ | ||||
| 		if(NSecondarySkill::names[id].compare(identifier) == 0) | ||||
| 		{ | ||||
| 			skill = new CSkill(SecondarySkill(id)); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(!skill) | ||||
| 	{ | ||||
| 		logMod->error("unknown secondary skill %s", identifier); | ||||
| 		throw std::runtime_error("invalid skill"); | ||||
| 	} | ||||
| 	CSkill * skill = new CSkill(SecondarySkill(index), identifier); | ||||
|  | ||||
| 	skill->name = json["name"].String(); | ||||
| 	switch(json["gainChance"].getType()) | ||||
| 	{ | ||||
| 	case JsonNode::JsonType::DATA_INTEGER: | ||||
| 		skill->gainChance[0] = json["gainChance"].Integer(); | ||||
| 		skill->gainChance[1] = json["gainChance"].Integer(); | ||||
| 		break; | ||||
| 	case JsonNode::JsonType::DATA_STRUCT: | ||||
| 		skill->gainChance[0] = json["gainChance"]["might"].Integer(); | ||||
| 		skill->gainChance[1] = json["gainChance"]["magic"].Integer(); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	for(int level = 1; level < NSecondarySkill::levels.size(); level++) | ||||
| 	{ | ||||
| 		const std::string & levelName = NSecondarySkill::levels[level]; // basic, advanced, expert | ||||
| @@ -182,10 +172,13 @@ CSkill * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & | ||||
| 		for(auto b : levelNode["effects"].Struct()) | ||||
| 		{ | ||||
| 			auto bonus = JsonUtils::parseBonus(b.second); | ||||
| 			bonus->sid = skill->id; | ||||
| 			skill->addNewBonus(bonus, level); | ||||
| 		} | ||||
| 		skill->setDescription(levelNode["description"].String(), level); | ||||
| 		CSkill::LevelInfo & skillAtLevel = skill->at(level); | ||||
| 		skillAtLevel.description = levelNode["description"].String(); | ||||
| 		skillAtLevel.iconSmall = levelNode["images"]["small"].String(); | ||||
| 		skillAtLevel.iconMedium = levelNode["images"]["medium"].String(); | ||||
| 		skillAtLevel.iconLarge = levelNode["images"]["large"].String(); | ||||
| 	} | ||||
| 	logMod->debug("loaded secondary skill %s(%d)", identifier, (int)skill->id); | ||||
| 	logMod->trace("%s", skill->toString()); | ||||
| @@ -220,3 +213,22 @@ std::vector<bool> CSkillHandler::getDefaultAllowed() const | ||||
| 	std::vector<bool> allowedSkills(objects.size(), true); | ||||
| 	return allowedSkills; | ||||
| } | ||||
|  | ||||
| si32 CSkillHandler::decodeSkill(const std::string & identifier) | ||||
| { | ||||
| 	auto rawId = VLC->modh->identifiers.getIdentifier("core", "skill", identifier); | ||||
| 	if(rawId) | ||||
| 		return rawId.get(); | ||||
| 	else | ||||
| 		return -1; | ||||
| } | ||||
|  | ||||
| std::string CSkillHandler::encodeSkill(const si32 index) | ||||
| { | ||||
| 	return (*VLC->skillh)[SecondarySkill(index)]->identifier; | ||||
| } | ||||
|  | ||||
| std::string CSkillHandler::encodeSkillWithType(const si32 index) | ||||
| { | ||||
| 	return CModHandler::makeFullIdentifier("", "skill", encodeSkill(index)); | ||||
| } | ||||
|   | ||||
| @@ -15,10 +15,13 @@ | ||||
|  | ||||
| class DLL_LINKAGE CSkill // secondary skill | ||||
| { | ||||
| protected: | ||||
| public: | ||||
| 	struct LevelInfo | ||||
| 	{ | ||||
| 		std::string description; //descriptions of spell for skill level | ||||
| 		std::string iconSmall; | ||||
| 		std::string iconMedium; | ||||
| 		std::string iconLarge; | ||||
| 		std::vector<std::shared_ptr<Bonus>> effects; | ||||
|  | ||||
| 		LevelInfo(); | ||||
| @@ -27,31 +30,43 @@ protected: | ||||
| 		template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 		{ | ||||
| 			h & description; | ||||
| 			if(version >= 785) | ||||
| 			{ | ||||
| 				h & iconSmall; | ||||
| 				h & iconMedium; | ||||
| 				h & iconLarge; | ||||
| 			} | ||||
| 			h & effects; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| private: | ||||
| 	std::vector<LevelInfo> levels; // bonuses provided by basic, advanced and expert level | ||||
| 	void addNewBonus(const std::shared_ptr<Bonus> & b, int level); | ||||
|  | ||||
| public: | ||||
| 	CSkill(SecondarySkill id = SecondarySkill::DEFAULT); | ||||
| 	CSkill(SecondarySkill id = SecondarySkill::DEFAULT, std::string identifier = "default"); | ||||
| 	~CSkill(); | ||||
|  | ||||
| 	void addNewBonus(const std::shared_ptr<Bonus> & b, int level); | ||||
| 	void setDescription(const std::string & desc, int level); | ||||
| 	const std::vector<std::shared_ptr<Bonus>> & getBonus(int level) const; | ||||
| 	const std::string & getDescription(int level) const; | ||||
| 	const LevelInfo & at(int level) const; | ||||
| 	LevelInfo & at(int level); | ||||
|  | ||||
| 	std::string toString() const; | ||||
|  | ||||
| 	SecondarySkill id; | ||||
| 	std::string identifier; | ||||
| 	std::string name; //as displayed in GUI | ||||
| 	std::array<si32, 2> gainChance; // gainChance[0/1] = default gain chance on level-up for might/magic heroes | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 	{ | ||||
| 		h & id; | ||||
| 		h & identifier; | ||||
| 		h & name; | ||||
| 		if(version >= 785) | ||||
| 		{ | ||||
| 			h & gainChance; | ||||
| 		} | ||||
| 		h & levels; | ||||
| 	} | ||||
|  | ||||
| @@ -72,11 +87,16 @@ public: | ||||
| 	void beforeValidate(JsonNode & object) override; | ||||
|  | ||||
| 	std::vector<bool> getDefaultAllowed() const override; | ||||
| 	const std::string getTypeName() const override; | ||||
| 	const std::vector<std::string> & getTypeNames() const override; | ||||
|  | ||||
| 	const std::string & skillInfo(int skill, int level) const; | ||||
| 	const std::string & skillName(int skill) const; | ||||
|  | ||||
| 	///json serialization helpers | ||||
| 	static si32 decodeSkill(const std::string & identifier); | ||||
| 	static std::string encodeSkill(const si32 index); | ||||
| 	static std::string encodeSkillWithType(const si32 index); | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 	{ | ||||
| 		h & objects; | ||||
|   | ||||
| @@ -1274,7 +1274,7 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype) | ||||
| 	case Bonus::PRIMARY_SKILL: | ||||
| 		return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]); | ||||
| 	case Bonus::SECONDARY_SKILL_PREMY: | ||||
| 		return JsonUtils::stringNode("skill." + NSecondarySkill::names[subtype]); | ||||
| 		return JsonUtils::stringNode(CSkillHandler::encodeSkillWithType(subtype)); | ||||
| 	case Bonus::SPECIAL_SPELL_LEV: | ||||
| 	case Bonus::SPECIFIC_SPELL_DAMAGE: | ||||
| 	case Bonus::SPECIAL_BLESS_DAMAGE: | ||||
| @@ -1362,7 +1362,7 @@ std::string Bonus::nameForBonus() const | ||||
| 	case Bonus::PRIMARY_SKILL: | ||||
| 		return PrimarySkill::names[subtype]; | ||||
| 	case Bonus::SECONDARY_SKILL_PREMY: | ||||
| 		return NSecondarySkill::names[subtype]; | ||||
| 		return CSkillHandler::encodeSkill(subtype); | ||||
| 	case Bonus::SPECIAL_SPELL_LEV: | ||||
| 	case Bonus::SPECIFIC_SPELL_DAMAGE: | ||||
| 	case Bonus::SPECIAL_BLESS_DAMAGE: | ||||
|   | ||||
| @@ -67,22 +67,22 @@ public: | ||||
| 	} | ||||
| 	void loadObject(std::string scope, std::string name, const JsonNode & data) override | ||||
| 	{ | ||||
| 		auto type_name = getTypeName(); | ||||
| 		auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), objects.size()); | ||||
|  | ||||
| 		objects.push_back(object); | ||||
|  | ||||
| 		registerObject(scope, type_name, name, object->id); | ||||
| 		for(auto type_name : getTypeNames()) | ||||
| 			registerObject(scope, type_name, name, object->id); | ||||
| 	} | ||||
| 	void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override | ||||
| 	{ | ||||
| 		auto type_name = getTypeName(); | ||||
| 		auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), index); | ||||
|  | ||||
| 		assert(objects[index] == nullptr); // ensure that this id was not loaded before | ||||
| 		objects[index] = object; | ||||
|  | ||||
| 		registerObject(scope,type_name, name, object->id); | ||||
| 		for(auto type_name : getTypeNames()) | ||||
| 			registerObject(scope, type_name, name, object->id); | ||||
| 	} | ||||
|  | ||||
| 	ConstTransitivePtr<_Object> operator[] (const _ObjectID id) const | ||||
| @@ -91,15 +91,19 @@ public: | ||||
|  | ||||
| 		if (raw_id < 0 || raw_id >= objects.size()) | ||||
| 		{ | ||||
| 			logMod->error("%s id %d is invalid", getTypeName(), static_cast<si64>(raw_id)); | ||||
| 			logMod->error("%s id %d is invalid", getTypeNames()[0], static_cast<si64>(raw_id)); | ||||
| 			throw std::runtime_error("internal error"); | ||||
| 		} | ||||
|  | ||||
| 		return objects[raw_id]; | ||||
| 	} | ||||
| 	size_t size() const | ||||
| 	{ | ||||
| 		return objects.size(); | ||||
| 	} | ||||
| protected: | ||||
| 	virtual _Object * loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) = 0; | ||||
| 	virtual const std::string getTypeName() const = 0; | ||||
| 	virtual const std::vector<std::string> & getTypeNames() const = 0; | ||||
| public: //todo: make private | ||||
| 	std::vector<ConstTransitivePtr<_Object>> objects; | ||||
| }; | ||||
|   | ||||
| @@ -560,7 +560,7 @@ void CGHeroInstance::recreateSpecialtyBonuses(std::vector<HeroSpecial *> & speci | ||||
| void CGHeroInstance::updateSkillBonus(SecondarySkill which, int val) | ||||
| { | ||||
| 	removeBonuses(Selector::source(Bonus::SECONDARY_SKILL, which)); | ||||
| 	auto skillBonus = (*VLC->skillh)[which]->getBonus(val); | ||||
| 	auto skillBonus = (*VLC->skillh)[which]->at(val).effects; | ||||
| 	for (auto b : skillBonus) | ||||
| 		addNewBonus(std::make_shared<Bonus>(*b)); | ||||
| } | ||||
| @@ -1107,7 +1107,7 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills() | ||||
| 	std::vector<SecondarySkill> skills; | ||||
| 	//picking sec. skills for choice | ||||
| 	std::set<SecondarySkill> basicAndAdv, expert, none; | ||||
| 	for(int i=0;i<GameConstants::SKILL_QUANTITY;i++) | ||||
| 	for(int i = 0; i < VLC->skillh->size(); i++) | ||||
| 		if (cb->isAllowed(2,i)) | ||||
| 			none.insert(SecondarySkill(i)); | ||||
|  | ||||
| @@ -1450,10 +1450,10 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler) | ||||
| 			{ | ||||
| 				const si32 rawId = p.first.num; | ||||
|  | ||||
| 				if(rawId < 0 || rawId >= GameConstants::SKILL_QUANTITY) | ||||
| 				if(rawId < 0 || rawId >= VLC->skillh->size()) | ||||
| 					logGlobal->error("Invalid secondary skill %d", rawId); | ||||
|  | ||||
| 				handler.serializeEnum(NSecondarySkill::names[rawId], p.second, 0, NSecondarySkill::levels); | ||||
| 				handler.serializeEnum((*VLC->skillh)[SecondarySkill(rawId)]->identifier, p.second, 0, NSecondarySkill::levels); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -1474,7 +1474,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler) | ||||
| 				const std::string skillId = p.first; | ||||
| 				const std::string levelId =  p.second.String(); | ||||
|  | ||||
| 				const int rawId = vstd::find_pos(NSecondarySkill::names, skillId); | ||||
| 				const int rawId = CSkillHandler::decodeSkill(skillId); | ||||
| 				if(rawId < 0) | ||||
| 				{ | ||||
| 					logGlobal->error("Invalid secondary skill %s", skillId); | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| #include "../CGameState.h" | ||||
| #include "CGTownInstance.h" | ||||
| #include "../CModHandler.h" | ||||
| #include "../CSkillHandler.h" | ||||
|  | ||||
| ///helpers | ||||
| static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1) | ||||
| @@ -299,7 +300,7 @@ void CGBlackMarket::newTurn(CRandomGenerator & rand) const | ||||
| void CGUniversity::initObj(CRandomGenerator & rand) | ||||
| { | ||||
| 	std::vector<int> toChoose; | ||||
| 	for(int i = 0; i < GameConstants::SKILL_QUANTITY; ++i) | ||||
| 	for(int i = 0; i < VLC->skillh->size(); ++i) | ||||
| 	{ | ||||
| 		if(cb->isAllowed(2, i)) | ||||
| 		{ | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include "../CSoundBase.h" | ||||
|  | ||||
| #include "../spells/CSpellHandler.h" | ||||
| #include "../CSkillHandler.h" | ||||
| #include "../StartInfo.h" | ||||
| #include "../IGameCallback.h" | ||||
| #include "../StringConstants.h" | ||||
| @@ -419,7 +420,7 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
|  | ||||
| 			for(size_t idx = 0; idx < abilities.size(); idx++) | ||||
| 			{ | ||||
| 				handler.serializeEnum(NSecondarySkill::names[abilities[idx]], abilityLevels[idx], NSecondarySkill::levels); | ||||
| 				handler.serializeEnum(CSkillHandler::encodeSkill(abilities[idx]), abilityLevels[idx], NSecondarySkill::levels); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -437,7 +438,7 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 			const std::string skillName = p.first; | ||||
| 			const std::string levelId = p.second.String(); | ||||
|  | ||||
| 			const int rawId = vstd::find_pos(NSecondarySkill::names, skillName); | ||||
| 			const int rawId = CSkillHandler::decodeSkill(skillName); | ||||
| 			if(rawId < 0) | ||||
| 			{ | ||||
| 				logGlobal->error("Invalid secondary skill %s", skillName); | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "../GameConstants.h" | ||||
| #include "../StringConstants.h" | ||||
| #include "../spells/CSpellHandler.h" | ||||
| #include "../CSkillHandler.h" | ||||
| #include "../mapping/CMap.h" | ||||
|  | ||||
|  | ||||
| @@ -920,7 +921,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 			identifier = PrimarySkill::names[rID]; | ||||
| 			break; | ||||
| 		case SECONDARY_SKILL: | ||||
| 			identifier = NSecondarySkill::names[rID]; | ||||
| 			identifier = CSkillHandler::encodeSkill(rID); | ||||
| 			break; | ||||
| 		case ARTIFACT: | ||||
| 			identifier = ArtifactID(rID).toArtifact()->identifier; | ||||
|   | ||||
| @@ -1439,7 +1439,7 @@ void CGWitchHut::initObj(CRandomGenerator & rand) | ||||
| { | ||||
| 	if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file | ||||
| 	{ | ||||
| 		for (int i = 0; i < GameConstants::SKILL_QUANTITY; i++) | ||||
| 		for(int i = 0; i < VLC->skillh->size(); i++) | ||||
| 			allowedAbilities.push_back(i); | ||||
| 	} | ||||
| 	ability = *RandomGeneratorUtil::nextItem(allowedAbilities, rand); | ||||
| @@ -1496,23 +1496,24 @@ void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 	//TODO: unify allowed abilities with others - make them std::vector<bool> | ||||
|  | ||||
| 	std::vector<bool> temp; | ||||
| 	temp.resize(GameConstants::SKILL_QUANTITY, false); | ||||
| 	size_t skillCount = VLC->skillh->size(); | ||||
| 	temp.resize(skillCount, false); | ||||
|  | ||||
| 	auto standard = VLC->heroh->getDefaultAllowedAbilities(); //todo: for WitchHut default is all except Leadership and Necromancy | ||||
| 	auto standard = VLC->skillh->getDefaultAllowed(); //todo: for WitchHut default is all except Leadership and Necromancy | ||||
|  | ||||
| 	if(handler.saving) | ||||
| 	{ | ||||
| 		for(si32 i = 0; i < GameConstants::SKILL_QUANTITY; ++i) | ||||
| 		for(si32 i = 0; i < skillCount; ++i) | ||||
| 			if(vstd::contains(allowedAbilities, i)) | ||||
| 				temp[i] = true; | ||||
| 	} | ||||
|  | ||||
| 	handler.serializeLIC("allowedSkills", &CHeroHandler::decodeSkill, &CHeroHandler::encodeSkill, standard, temp); | ||||
| 	handler.serializeLIC("allowedSkills", &CSkillHandler::decodeSkill, &CSkillHandler::encodeSkill, standard, temp); | ||||
|  | ||||
| 	if(!handler.saving) | ||||
| 	{ | ||||
| 		allowedAbilities.clear(); | ||||
| 		for (si32 i=0; i<temp.size(); i++) | ||||
| 		for(si32 i = 0; i < skillCount; ++i) | ||||
| 			if(temp[i]) | ||||
| 				allowedAbilities.push_back(i); | ||||
| 	} | ||||
| @@ -1746,7 +1747,7 @@ void CGScholar::initObj(CRandomGenerator & rand) | ||||
| 			bonusID = rand.nextInt(GameConstants::PRIMARY_SKILLS -1); | ||||
| 			break; | ||||
| 		case SECONDARY_SKILL: | ||||
| 			bonusID = rand.nextInt(GameConstants::SKILL_QUANTITY -1); | ||||
| 			bonusID = rand.nextInt(VLC->skillh->size() - 1); | ||||
| 			break; | ||||
| 		case SPELL: | ||||
| 			std::vector<SpellID> possibilities; | ||||
| @@ -1770,7 +1771,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| 			handler.serializeString("rewardPrimSkill", value); | ||||
| 			break; | ||||
| 		case SECONDARY_SKILL: | ||||
| 			value = NSecondarySkill::names[bonusID]; | ||||
| 			value = CSkillHandler::encodeSkill(bonusID); | ||||
| 			handler.serializeString("rewardSkill", value); | ||||
| 			break; | ||||
| 		case SPELL: | ||||
|   | ||||
| @@ -19,6 +19,7 @@ | ||||
| #include "../mapObjects/CGHeroInstance.h" | ||||
| #include "../CGeneralTextHandler.h" | ||||
| #include "../spells/CSpellHandler.h" | ||||
| #include "../CSkillHandler.h" | ||||
| #include "CMapEditManager.h" | ||||
| #include "../serializer/JsonSerializeFormat.h" | ||||
|  | ||||
| @@ -240,7 +241,7 @@ CMap::CMap() | ||||
| 	guardingCreaturePositions(nullptr) | ||||
| { | ||||
| 	allHeroes.resize(allowedHeroes.size()); | ||||
| 	allowedAbilities = VLC->heroh->getDefaultAllowedAbilities(); | ||||
| 	allowedAbilities = VLC->skillh->getDefaultAllowed(); | ||||
| 	allowedArtifact = VLC->arth->getDefaultAllowed(); | ||||
| 	allowedSpell = VLC->spellh->getDefaultAllowed(); | ||||
| } | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| #include "../CStopWatch.h" | ||||
| #include "../filesystem/Filesystem.h" | ||||
| #include "../spells/CSpellHandler.h" | ||||
| #include "../CSkillHandler.h" | ||||
| #include "../CCreatureHandler.h" | ||||
| #include "../CGeneralTextHandler.h" | ||||
| #include "../CHeroHandler.h" | ||||
| @@ -1144,14 +1145,18 @@ void CMapLoaderH3M::readObjects() | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 					// enable new (modded) skills | ||||
| 					if(wh->allowedAbilities.size() != 1) | ||||
| 					{ | ||||
| 						for(int skillID = GameConstants::SKILL_QUANTITY; skillID < VLC->skillh->size(); ++skillID) | ||||
| 							wh->allowedAbilities.push_back(skillID); | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					// RoE map | ||||
| 					for(int gg = 0; gg < GameConstants::SKILL_QUANTITY; ++gg) | ||||
| 					{ | ||||
| 						wh->allowedAbilities.push_back(gg); | ||||
| 					} | ||||
| 					for(int skillID = 0; skillID < VLC->skillh->size(); ++skillID) | ||||
| 						wh->allowedAbilities.push_back(skillID); | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
|   | ||||
| @@ -25,6 +25,7 @@ | ||||
| #include "../mapObjects/CGHeroInstance.h" | ||||
| #include "../mapObjects/CGTownInstance.h" | ||||
| #include "../spells/CSpellHandler.h" | ||||
| #include "../CSkillHandler.h" | ||||
| #include "../StringConstants.h" | ||||
| #include "../serializer/JsonDeserializer.h" | ||||
| #include "../serializer/JsonSerializer.h" | ||||
| @@ -807,7 +808,7 @@ void CMapFormatJson::serializeOptions(JsonSerializeFormat & handler) | ||||
|  | ||||
| 	serializePredefinedHeroes(handler); | ||||
|  | ||||
| 	handler.serializeLIC("allowedAbilities", &CHeroHandler::decodeSkill, &CHeroHandler::encodeSkill, VLC->heroh->getDefaultAllowedAbilities(), map->allowedAbilities); | ||||
| 	handler.serializeLIC("allowedAbilities", &CSkillHandler::decodeSkill, &CSkillHandler::encodeSkill, VLC->skillh->getDefaultAllowed(), map->allowedAbilities); | ||||
|  | ||||
| 	handler.serializeLIC("allowedArtifacts",  &ArtifactID::decode, &ArtifactID::encode, VLC->arth->getDefaultAllowed(), map->allowedArtifact); | ||||
|  | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| #include "../ConstTransitivePtr.h" | ||||
| #include "../GameConstants.h" | ||||
|  | ||||
| const ui32 SERIALIZATION_VERSION = 784; | ||||
| const ui32 SERIALIZATION_VERSION = 785; | ||||
| const ui32 MINIMAL_SERIALIZATION_VERSION = 753; | ||||
| const std::string SAVEGAME_MAGIC = "VCMISVG"; | ||||
|  | ||||
|   | ||||
| @@ -707,9 +707,10 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize) | ||||
| 	return legacyData; | ||||
| } | ||||
|  | ||||
| const std::string CSpellHandler::getTypeName() const | ||||
| const std::vector<std::string> & CSpellHandler::getTypeNames() const | ||||
| { | ||||
| 	return "spell"; | ||||
| 	static const std::vector<std::string> typeNames = { "spell" }; | ||||
| 	return typeNames; | ||||
| } | ||||
|  | ||||
| CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) | ||||
|   | ||||
| @@ -441,7 +441,7 @@ public: | ||||
| 	 */ | ||||
| 	std::vector<bool> getDefaultAllowed() const override; | ||||
|  | ||||
| 	const std::string getTypeName() const override; | ||||
| 	const std::vector<std::string> & getTypeNames() const override; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler & h, const int version) | ||||
| 	{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user