diff --git a/ChangeLog b/ChangeLog index 47362e5c3..1716ea4d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/client/Graphics.cpp b/client/Graphics.cpp index c0b7ad440..186f63a4b 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -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); + } + } } diff --git a/config/schemas/skill.json b/config/schemas/skill.json index 0fe64298b..24e5013d2 100644 --- a/config/schemas/skill.json +++ b/config/schemas/skill.json @@ -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", diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 00bec6032..e034f27e8 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -20,6 +20,7 @@ #include "CModHandler.h" #include "CTownHandler.h" #include "mapObjects/CObjectHandler.h" //for hero specialty +#include "CSkillHandler.h" #include #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(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 CHeroHandler::getDefaultAllowed() const return allowedHeroes; } -std::vector CHeroHandler::getDefaultAllowedAbilities() const -{ - std::vector 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]; -} diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index 65fab7b67..1608efa5a 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -311,25 +311,12 @@ public: std::vector 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 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 void serialize(Handler &h, const int version) { h & classes; diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index ba9244b3d..c9501ee02 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -240,7 +240,7 @@ std::vector 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); } } diff --git a/lib/CSkillHandler.cpp b/lib/CSkillHandler.cpp index a8d4e4d6a..9d8123f3e 100644 --- a/lib/CSkillHandler.cpp +++ b/lib/CSkillHandler.cpp @@ -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 & 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> & 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 CSkillHandler::loadLegacyData(size_t dataSize) return legacyData; } -const std::string CSkillHandler::getTypeName() const +const std::vector & CSkillHandler::getTypeNames() const { - return "skill"; + static const std::vector 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 CSkillHandler::getDefaultAllowed() const std::vector 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)); +} diff --git a/lib/CSkillHandler.h b/lib/CSkillHandler.h index 1e6d1362f..c3a3519c1 100644 --- a/lib/CSkillHandler.h +++ b/lib/CSkillHandler.h @@ -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> effects; LevelInfo(); @@ -27,31 +30,43 @@ protected: template void serialize(Handler & h, const int version) { h & description; + if(version >= 785) + { + h & iconSmall; + h & iconMedium; + h & iconLarge; + } h & effects; } }; +private: std::vector levels; // bonuses provided by basic, advanced and expert level + void addNewBonus(const std::shared_ptr & 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 & b, int level); - void setDescription(const std::string & desc, int level); - const std::vector> & 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 gainChance; // gainChance[0/1] = default gain chance on level-up for might/magic heroes template 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 getDefaultAllowed() const override; - const std::string getTypeName() const override; + const std::vector & 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 void serialize(Handler & h, const int version) { h & objects; diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 2426580cb..61763d445 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -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: diff --git a/lib/IHandlerBase.h b/lib/IHandlerBase.h index c6ed04ced..74496689f 100644 --- a/lib/IHandlerBase.h +++ b/lib/IHandlerBase.h @@ -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(raw_id)); + logMod->error("%s id %d is invalid", getTypeNames()[0], static_cast(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 & getTypeNames() const = 0; public: //todo: make private std::vector> objects; }; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 1f3d3e1f1..1269f83d6 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -560,7 +560,7 @@ void CGHeroInstance::recreateSpecialtyBonuses(std::vector & 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(*b)); } @@ -1107,7 +1107,7 @@ std::vector CGHeroInstance::getLevelUpProposedSecondarySkills() std::vector skills; //picking sec. skills for choice std::set basicAndAdv, expert, none; - for(int i=0;iskillh->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); diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index ecb6e6ccf..462037785 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -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 toChoose; - for(int i = 0; i < GameConstants::SKILL_QUANTITY; ++i) + for(int i = 0; i < VLC->skillh->size(); ++i) { if(cb->isAllowed(2, i)) { diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 547593306..d6c432432 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -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); diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index c6a551148..9fa781aaf 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -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; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index f9648caa8..64476814d 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -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 std::vector 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; iskillh->size() - 1); break; case SPELL: std::vector 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: diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index ddf431337..b3e19466b 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -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(); } diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index d5ae329d4..4f75499ef 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -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; } diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 55178f4d7..adc75f255 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -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); diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 705a5a907..1cb4975de 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -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"; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index a16c58e13..4cd646404 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -707,9 +707,10 @@ std::vector CSpellHandler::loadLegacyData(size_t dataSize) return legacyData; } -const std::string CSpellHandler::getTypeName() const +const std::vector & CSpellHandler::getTypeNames() const { - return "spell"; + static const std::vector typeNames = { "spell" }; + return typeNames; } CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 44e4fd4a4..f86062721 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -441,7 +441,7 @@ public: */ std::vector getDefaultAllowed() const override; - const std::string getTypeName() const override; + const std::vector & getTypeNames() const override; template void serialize(Handler & h, const int version) {