1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +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:
Henning Koehler 2018-03-31 18:56:40 +13:00 committed by ArseniyShestakov
parent b09a54fa9c
commit 6ddcb079a4
21 changed files with 207 additions and 124 deletions

View File

@ -25,7 +25,7 @@ SPELLS:
MODS: MODS:
* Improve support for WoG commander artifacts and skill descriptions * 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 * Map object sounds can now be configured via json
* Added bonus updaters for hero specialties * Added bonus updaters for hero specialties

View File

@ -24,6 +24,7 @@
#include "../lib/CGeneralTextHandler.h" #include "../lib/CGeneralTextHandler.h"
#include "../lib/CCreatureHandler.h" #include "../lib/CCreatureHandler.h"
#include "CBitmapHandler.h" #include "CBitmapHandler.h"
#include "../lib/CSkillHandler.h"
#include "../lib/spells/CSpellHandler.h" #include "../lib/spells/CSpellHandler.h"
#include "../lib/CGameState.h" #include "../lib/CGameState.h"
#include "../lib/JsonNode.h" #include "../lib/JsonNode.h"
@ -443,4 +444,16 @@ void Graphics::initializeImageLists()
addImageListEntry(spell->id, "SPELLBON", spell->iconScenarioBonus); addImageListEntry(spell->id, "SPELLBON", spell->iconScenarioBonus);
addImageListEntry(spell->id, "SPELLSCR", spell->iconScroll); 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);
}
}
} }

View File

@ -13,6 +13,27 @@
"description" : "Set of bonuses provided by skill at given level", "description" : "Set of bonuses provided by skill at given level",
"required" : ["description", "effects"], "required" : ["description", "effects"],
"properties" : { "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" : { "description" : {
"type" : "string", "type" : "string",
"description" : "localizable description" "description" : "localizable description"
@ -39,6 +60,28 @@
"type": "string", "type": "string",
"description": "localizable skill name" "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" : { "base" : {
"type" : "object", "type" : "object",
"description" : "will be merged with all levels", "description" : "will be merged with all levels",

View File

@ -20,6 +20,7 @@
#include "CModHandler.h" #include "CModHandler.h"
#include "CTownHandler.h" #include "CTownHandler.h"
#include "mapObjects/CObjectHandler.h" //for hero specialty #include "mapObjects/CObjectHandler.h" //for hero specialty
#include "CSkillHandler.h"
#include <math.h> #include <math.h>
#include "mapObjects/CObjectClassesHandler.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()); 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"], VLC->modh->identifiers.requestIdentifier ("creature", node["commander"],
@ -241,6 +248,17 @@ void CHeroClassHandler::afterLoadFinalization()
float chance = heroClass->defaultTavernChance * faction->town->defaultTavernChance; 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 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) for (CHeroClass * hc : heroClasses)
@ -277,11 +295,6 @@ CHeroHandler::CHeroHandler()
{ {
VLC->heroh = this; 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(); loadObstacles();
loadTerrains(); loadTerrains();
for (int i = 0; i < GameConstants::TERRAIN_TYPES; ++i) for (int i = 0; i < GameConstants::TERRAIN_TYPES; ++i)
@ -943,13 +956,6 @@ std::vector<bool> CHeroHandler::getDefaultAllowed() const
return allowedHeroes; 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) si32 CHeroHandler::decodeHero(const std::string & identifier)
{ {
auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", 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; 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];
}

View File

@ -311,25 +311,12 @@ public:
std::vector<bool> getDefaultAllowed() const override; 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 ///json serialization helper
static si32 decodeHero(const std::string & identifier); static si32 decodeHero(const std::string & identifier);
///json serialization helper ///json serialization helper
static std::string encodeHero(const si32 index); 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) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & classes; h & classes;

View File

@ -240,7 +240,7 @@ std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdent
{ {
// allow only available to all core mod or dependencies // allow only available to all core mod or dependencies
auto myDeps = VLC->modh->getModData(request.localScope).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); allowedScopes.insert(request.remoteScope);
} }
} }

View File

@ -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) levels.resize(NSecondarySkill::levels.size() - 1);
identifier = "default";
else
identifier = NSecondarySkill::names[id];
// init levels
LevelInfo emptyLevel;
for(int level = 1; level < NSecondarySkill::levels.size(); level++)
levels.push_back(emptyLevel);
} }
CSkill::~CSkill() CSkill::~CSkill()
@ -56,19 +50,16 @@ void CSkill::addNewBonus(const std::shared_ptr<Bonus> & b, int level)
levels[level-1].effects.push_back(b); 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; assert(1 <= level && level < NSecondarySkill::levels.size());
} return levels[level - 1];
const std::string & CSkill::getDescription(int level) const
{
return levels[level-1].description;
} }
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info) 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; 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 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 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 * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index)
{ {
CSkill * skill = nullptr; CSkill * skill = new CSkill(SecondarySkill(index), identifier);
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");
}
skill->name = json["name"].String(); 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++) for(int level = 1; level < NSecondarySkill::levels.size(); level++)
{ {
const std::string & levelName = NSecondarySkill::levels[level]; // basic, advanced, expert 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()) for(auto b : levelNode["effects"].Struct())
{ {
auto bonus = JsonUtils::parseBonus(b.second); auto bonus = JsonUtils::parseBonus(b.second);
bonus->sid = skill->id;
skill->addNewBonus(bonus, level); 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->debug("loaded secondary skill %s(%d)", identifier, (int)skill->id);
logMod->trace("%s", skill->toString()); logMod->trace("%s", skill->toString());
@ -220,3 +213,22 @@ std::vector<bool> CSkillHandler::getDefaultAllowed() const
std::vector<bool> allowedSkills(objects.size(), true); std::vector<bool> allowedSkills(objects.size(), true);
return allowedSkills; 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));
}

View File

@ -15,10 +15,13 @@
class DLL_LINKAGE CSkill // secondary skill class DLL_LINKAGE CSkill // secondary skill
{ {
protected: public:
struct LevelInfo struct LevelInfo
{ {
std::string description; //descriptions of spell for skill level 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; std::vector<std::shared_ptr<Bonus>> effects;
LevelInfo(); LevelInfo();
@ -27,31 +30,43 @@ protected:
template <typename Handler> void serialize(Handler & h, const int version) template <typename Handler> void serialize(Handler & h, const int version)
{ {
h & description; h & description;
if(version >= 785)
{
h & iconSmall;
h & iconMedium;
h & iconLarge;
}
h & effects; h & effects;
} }
}; };
private:
std::vector<LevelInfo> levels; // bonuses provided by basic, advanced and expert level std::vector<LevelInfo> levels; // bonuses provided by basic, advanced and expert level
void addNewBonus(const std::shared_ptr<Bonus> & b, int level);
public: public:
CSkill(SecondarySkill id = SecondarySkill::DEFAULT); CSkill(SecondarySkill id = SecondarySkill::DEFAULT, std::string identifier = "default");
~CSkill(); ~CSkill();
void addNewBonus(const std::shared_ptr<Bonus> & b, int level); const LevelInfo & at(int level) const;
void setDescription(const std::string & desc, int level); LevelInfo & at(int level);
const std::vector<std::shared_ptr<Bonus>> & getBonus(int level) const;
const std::string & getDescription(int level) const;
std::string toString() const; std::string toString() const;
SecondarySkill id; SecondarySkill id;
std::string identifier; std::string identifier;
std::string name; //as displayed in GUI 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) template <typename Handler> void serialize(Handler & h, const int version)
{ {
h & id; h & id;
h & identifier; h & identifier;
h & name; h & name;
if(version >= 785)
{
h & gainChance;
}
h & levels; h & levels;
} }
@ -72,11 +87,16 @@ public:
void beforeValidate(JsonNode & object) override; void beforeValidate(JsonNode & object) override;
std::vector<bool> getDefaultAllowed() const 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 & skillInfo(int skill, int level) const;
const std::string & skillName(int skill) 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) template <typename Handler> void serialize(Handler & h, const int version)
{ {
h & objects; h & objects;

View File

@ -1274,7 +1274,7 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype)
case Bonus::PRIMARY_SKILL: case Bonus::PRIMARY_SKILL:
return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]); return JsonUtils::stringNode("primSkill." + PrimarySkill::names[subtype]);
case Bonus::SECONDARY_SKILL_PREMY: 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::SPECIAL_SPELL_LEV:
case Bonus::SPECIFIC_SPELL_DAMAGE: case Bonus::SPECIFIC_SPELL_DAMAGE:
case Bonus::SPECIAL_BLESS_DAMAGE: case Bonus::SPECIAL_BLESS_DAMAGE:
@ -1362,7 +1362,7 @@ std::string Bonus::nameForBonus() const
case Bonus::PRIMARY_SKILL: case Bonus::PRIMARY_SKILL:
return PrimarySkill::names[subtype]; return PrimarySkill::names[subtype];
case Bonus::SECONDARY_SKILL_PREMY: case Bonus::SECONDARY_SKILL_PREMY:
return NSecondarySkill::names[subtype]; return CSkillHandler::encodeSkill(subtype);
case Bonus::SPECIAL_SPELL_LEV: case Bonus::SPECIAL_SPELL_LEV:
case Bonus::SPECIFIC_SPELL_DAMAGE: case Bonus::SPECIFIC_SPELL_DAMAGE:
case Bonus::SPECIAL_BLESS_DAMAGE: case Bonus::SPECIAL_BLESS_DAMAGE:

View File

@ -67,21 +67,21 @@ public:
} }
void loadObject(std::string scope, std::string name, const JsonNode & data) override 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()); auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), objects.size());
objects.push_back(object); objects.push_back(object);
for(auto type_name : getTypeNames())
registerObject(scope, type_name, name, object->id); registerObject(scope, type_name, name, object->id);
} }
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override 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); auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), index);
assert(objects[index] == nullptr); // ensure that this id was not loaded before assert(objects[index] == nullptr); // ensure that this id was not loaded before
objects[index] = object; objects[index] = object;
for(auto type_name : getTypeNames())
registerObject(scope, type_name, name, object->id); registerObject(scope, type_name, name, object->id);
} }
@ -91,15 +91,19 @@ public:
if (raw_id < 0 || raw_id >= objects.size()) 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"); throw std::runtime_error("internal error");
} }
return objects[raw_id]; return objects[raw_id];
} }
size_t size() const
{
return objects.size();
}
protected: protected:
virtual _Object * loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) = 0; 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 public: //todo: make private
std::vector<ConstTransitivePtr<_Object>> objects; std::vector<ConstTransitivePtr<_Object>> objects;
}; };

View File

@ -560,7 +560,7 @@ void CGHeroInstance::recreateSpecialtyBonuses(std::vector<HeroSpecial *> & speci
void CGHeroInstance::updateSkillBonus(SecondarySkill which, int val) void CGHeroInstance::updateSkillBonus(SecondarySkill which, int val)
{ {
removeBonuses(Selector::source(Bonus::SECONDARY_SKILL, which)); 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) for (auto b : skillBonus)
addNewBonus(std::make_shared<Bonus>(*b)); addNewBonus(std::make_shared<Bonus>(*b));
} }
@ -1107,7 +1107,7 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills()
std::vector<SecondarySkill> skills; std::vector<SecondarySkill> skills;
//picking sec. skills for choice //picking sec. skills for choice
std::set<SecondarySkill> basicAndAdv, expert, none; 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)) if (cb->isAllowed(2,i))
none.insert(SecondarySkill(i)); none.insert(SecondarySkill(i));
@ -1450,10 +1450,10 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
{ {
const si32 rawId = p.first.num; 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); 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 skillId = p.first;
const std::string levelId = p.second.String(); 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) if(rawId < 0)
{ {
logGlobal->error("Invalid secondary skill %s", skillId); logGlobal->error("Invalid secondary skill %s", skillId);

View File

@ -18,6 +18,7 @@
#include "../CGameState.h" #include "../CGameState.h"
#include "CGTownInstance.h" #include "CGTownInstance.h"
#include "../CModHandler.h" #include "../CModHandler.h"
#include "../CSkillHandler.h"
///helpers ///helpers
static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1) 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) void CGUniversity::initObj(CRandomGenerator & rand)
{ {
std::vector<int> toChoose; 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)) if(cb->isAllowed(2, i))
{ {

View File

@ -15,6 +15,7 @@
#include "../CSoundBase.h" #include "../CSoundBase.h"
#include "../spells/CSpellHandler.h" #include "../spells/CSpellHandler.h"
#include "../CSkillHandler.h"
#include "../StartInfo.h" #include "../StartInfo.h"
#include "../IGameCallback.h" #include "../IGameCallback.h"
#include "../StringConstants.h" #include "../StringConstants.h"
@ -419,7 +420,7 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler)
for(size_t idx = 0; idx < abilities.size(); idx++) 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 skillName = p.first;
const std::string levelId = p.second.String(); 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) if(rawId < 0)
{ {
logGlobal->error("Invalid secondary skill %s", skillName); logGlobal->error("Invalid secondary skill %s", skillName);

View File

@ -24,6 +24,7 @@
#include "../GameConstants.h" #include "../GameConstants.h"
#include "../StringConstants.h" #include "../StringConstants.h"
#include "../spells/CSpellHandler.h" #include "../spells/CSpellHandler.h"
#include "../CSkillHandler.h"
#include "../mapping/CMap.h" #include "../mapping/CMap.h"
@ -920,7 +921,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
identifier = PrimarySkill::names[rID]; identifier = PrimarySkill::names[rID];
break; break;
case SECONDARY_SKILL: case SECONDARY_SKILL:
identifier = NSecondarySkill::names[rID]; identifier = CSkillHandler::encodeSkill(rID);
break; break;
case ARTIFACT: case ARTIFACT:
identifier = ArtifactID(rID).toArtifact()->identifier; identifier = ArtifactID(rID).toArtifact()->identifier;

View File

@ -1439,7 +1439,7 @@ void CGWitchHut::initObj(CRandomGenerator & rand)
{ {
if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file 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); allowedAbilities.push_back(i);
} }
ability = *RandomGeneratorUtil::nextItem(allowedAbilities, rand); 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> //TODO: unify allowed abilities with others - make them std::vector<bool>
std::vector<bool> temp; 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) if(handler.saving)
{ {
for(si32 i = 0; i < GameConstants::SKILL_QUANTITY; ++i) for(si32 i = 0; i < skillCount; ++i)
if(vstd::contains(allowedAbilities, i)) if(vstd::contains(allowedAbilities, i))
temp[i] = true; temp[i] = true;
} }
handler.serializeLIC("allowedSkills", &CHeroHandler::decodeSkill, &CHeroHandler::encodeSkill, standard, temp); handler.serializeLIC("allowedSkills", &CSkillHandler::decodeSkill, &CSkillHandler::encodeSkill, standard, temp);
if(!handler.saving) if(!handler.saving)
{ {
allowedAbilities.clear(); allowedAbilities.clear();
for (si32 i=0; i<temp.size(); i++) for(si32 i = 0; i < skillCount; ++i)
if(temp[i]) if(temp[i])
allowedAbilities.push_back(i); allowedAbilities.push_back(i);
} }
@ -1746,7 +1747,7 @@ void CGScholar::initObj(CRandomGenerator & rand)
bonusID = rand.nextInt(GameConstants::PRIMARY_SKILLS -1); bonusID = rand.nextInt(GameConstants::PRIMARY_SKILLS -1);
break; break;
case SECONDARY_SKILL: case SECONDARY_SKILL:
bonusID = rand.nextInt(GameConstants::SKILL_QUANTITY -1); bonusID = rand.nextInt(VLC->skillh->size() - 1);
break; break;
case SPELL: case SPELL:
std::vector<SpellID> possibilities; std::vector<SpellID> possibilities;
@ -1770,7 +1771,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
handler.serializeString("rewardPrimSkill", value); handler.serializeString("rewardPrimSkill", value);
break; break;
case SECONDARY_SKILL: case SECONDARY_SKILL:
value = NSecondarySkill::names[bonusID]; value = CSkillHandler::encodeSkill(bonusID);
handler.serializeString("rewardSkill", value); handler.serializeString("rewardSkill", value);
break; break;
case SPELL: case SPELL:

View File

@ -19,6 +19,7 @@
#include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGHeroInstance.h"
#include "../CGeneralTextHandler.h" #include "../CGeneralTextHandler.h"
#include "../spells/CSpellHandler.h" #include "../spells/CSpellHandler.h"
#include "../CSkillHandler.h"
#include "CMapEditManager.h" #include "CMapEditManager.h"
#include "../serializer/JsonSerializeFormat.h" #include "../serializer/JsonSerializeFormat.h"
@ -240,7 +241,7 @@ CMap::CMap()
guardingCreaturePositions(nullptr) guardingCreaturePositions(nullptr)
{ {
allHeroes.resize(allowedHeroes.size()); allHeroes.resize(allowedHeroes.size());
allowedAbilities = VLC->heroh->getDefaultAllowedAbilities(); allowedAbilities = VLC->skillh->getDefaultAllowed();
allowedArtifact = VLC->arth->getDefaultAllowed(); allowedArtifact = VLC->arth->getDefaultAllowed();
allowedSpell = VLC->spellh->getDefaultAllowed(); allowedSpell = VLC->spellh->getDefaultAllowed();
} }

View File

@ -17,6 +17,7 @@
#include "../CStopWatch.h" #include "../CStopWatch.h"
#include "../filesystem/Filesystem.h" #include "../filesystem/Filesystem.h"
#include "../spells/CSpellHandler.h" #include "../spells/CSpellHandler.h"
#include "../CSkillHandler.h"
#include "../CCreatureHandler.h" #include "../CCreatureHandler.h"
#include "../CGeneralTextHandler.h" #include "../CGeneralTextHandler.h"
#include "../CHeroHandler.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 else
{ {
// RoE map // RoE map
for(int gg = 0; gg < GameConstants::SKILL_QUANTITY; ++gg) for(int skillID = 0; skillID < VLC->skillh->size(); ++skillID)
{ wh->allowedAbilities.push_back(skillID);
wh->allowedAbilities.push_back(gg);
}
} }
break; break;
} }

View File

@ -25,6 +25,7 @@
#include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGHeroInstance.h"
#include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CGTownInstance.h"
#include "../spells/CSpellHandler.h" #include "../spells/CSpellHandler.h"
#include "../CSkillHandler.h"
#include "../StringConstants.h" #include "../StringConstants.h"
#include "../serializer/JsonDeserializer.h" #include "../serializer/JsonDeserializer.h"
#include "../serializer/JsonSerializer.h" #include "../serializer/JsonSerializer.h"
@ -807,7 +808,7 @@ void CMapFormatJson::serializeOptions(JsonSerializeFormat & handler)
serializePredefinedHeroes(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); handler.serializeLIC("allowedArtifacts", &ArtifactID::decode, &ArtifactID::encode, VLC->arth->getDefaultAllowed(), map->allowedArtifact);

View File

@ -12,7 +12,7 @@
#include "../ConstTransitivePtr.h" #include "../ConstTransitivePtr.h"
#include "../GameConstants.h" #include "../GameConstants.h"
const ui32 SERIALIZATION_VERSION = 784; const ui32 SERIALIZATION_VERSION = 785;
const ui32 MINIMAL_SERIALIZATION_VERSION = 753; const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
const std::string SAVEGAME_MAGIC = "VCMISVG"; const std::string SAVEGAME_MAGIC = "VCMISVG";

View File

@ -707,9 +707,10 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
return legacyData; 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) CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index)

View File

@ -441,7 +441,7 @@ public:
*/ */
std::vector<bool> getDefaultAllowed() const override; 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) template <typename Handler> void serialize(Handler & h, const int version)
{ {