mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
Merge pull request #5148 from Laserlicht/h3c_vcmp_converter
[1.6.x] h3c to vcmp converter
This commit is contained in:
9
Global.h
9
Global.h
@@ -369,6 +369,15 @@ namespace vstd
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// given a map from keys to values, creates a new map from values to keys
|
||||||
|
template<typename K, typename V>
|
||||||
|
static std::map<V, K> reverseMap(const std::map<K, V>& m) {
|
||||||
|
std::map<V, K> r;
|
||||||
|
for (const auto& kv : m)
|
||||||
|
r[kv.second] = kv.first;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
//returns first key that maps to given value if present, returns success via found if provided
|
//returns first key that maps to given value if present, returns success via found if provided
|
||||||
template <typename Key, typename T>
|
template <typename Key, typename T>
|
||||||
Key findKey(const std::map<Key, T> & map, const T & value, bool * found = nullptr)
|
Key findKey(const std::map<Key, T> & map, const T & value, bool * found = nullptr)
|
||||||
|
|||||||
@@ -171,6 +171,26 @@ void CampaignHandler::readHeaderFromJson(CampaignHeader & ret, JsonNode & reader
|
|||||||
ret.outroVideo = VideoPath::fromJson(reader["outroVideo"]);
|
ret.outroVideo = VideoPath::fromJson(reader["outroVideo"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonNode CampaignHandler::writeHeaderToJson(CampaignHeader & header)
|
||||||
|
{
|
||||||
|
JsonNode node;
|
||||||
|
node["version"].Integer() = static_cast<ui64>(CampaignVersion::VCMI);
|
||||||
|
node["regions"] = CampaignRegions::toJson(header.campaignRegions);
|
||||||
|
node["name"].String() = header.name.toString();
|
||||||
|
node["description"].String() = header.description.toString();
|
||||||
|
node["author"].String() = header.author.toString();
|
||||||
|
node["authorContact"].String() = header.authorContact.toString();
|
||||||
|
node["campaignVersion"].String() = header.campaignVersion.toString();
|
||||||
|
node["creationDateTime"].Integer() = header.creationDateTime;
|
||||||
|
node["allowDifficultySelection"].Bool() = header.difficultyChosenByPlayer;
|
||||||
|
node["music"].String() = header.music.getName();
|
||||||
|
node["loadingBackground"].String() = header.loadingBackground.getName();
|
||||||
|
node["videoRim"].String() = header.videoRim.getName();
|
||||||
|
node["introVideo"].String() = header.introVideo.getName();
|
||||||
|
node["outroVideo"].String() = header.outroVideo.getName();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader)
|
CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader)
|
||||||
{
|
{
|
||||||
auto prologEpilogReader = [](JsonNode & identifier) -> CampaignScenarioPrologEpilog
|
auto prologEpilogReader = [](JsonNode & identifier) -> CampaignScenarioPrologEpilog
|
||||||
@@ -203,56 +223,86 @@ CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonNode CampaignHandler::writeScenarioToJson(const CampaignScenario & scenario)
|
||||||
|
{
|
||||||
|
auto prologEpilogWriter = [](const CampaignScenarioPrologEpilog & elem) -> JsonNode
|
||||||
|
{
|
||||||
|
JsonNode node;
|
||||||
|
if(elem.hasPrologEpilog)
|
||||||
|
{
|
||||||
|
node["video"].String() = elem.prologVideo.getName();
|
||||||
|
node["music"].String() = elem.prologMusic.getName();
|
||||||
|
node["voice"].String() = elem.prologVoice.getName();
|
||||||
|
node["text"].String() = elem.prologText.toString();
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
|
||||||
|
JsonNode node;
|
||||||
|
node["map"].String() = scenario.mapName;
|
||||||
|
for(auto & g : scenario.preconditionRegions)
|
||||||
|
node["preconditions"].Vector().push_back(JsonNode(static_cast<ui32>(g)));
|
||||||
|
node["color"].Integer() = scenario.regionColor;
|
||||||
|
node["difficulty"].Integer() = scenario.difficulty;
|
||||||
|
node["regionText"].String() = scenario.regionText.toString();
|
||||||
|
node["prolog"] = prologEpilogWriter(scenario.prolog);
|
||||||
|
node["epilog"] = prologEpilogWriter(scenario.epilog);
|
||||||
|
|
||||||
|
writeScenarioTravelToJson(node, scenario.travelOptions);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::map<std::string, CampaignStartOptions> startOptionsMap = {
|
||||||
|
{"none", CampaignStartOptions::NONE},
|
||||||
|
{"bonus", CampaignStartOptions::START_BONUS},
|
||||||
|
{"crossover", CampaignStartOptions::HERO_CROSSOVER},
|
||||||
|
{"hero", CampaignStartOptions::HERO_OPTIONS}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::string, CampaignBonusType> bonusTypeMap = {
|
||||||
|
{"spell", CampaignBonusType::SPELL},
|
||||||
|
{"creature", CampaignBonusType::MONSTER},
|
||||||
|
{"building", CampaignBonusType::BUILDING},
|
||||||
|
{"artifact", CampaignBonusType::ARTIFACT},
|
||||||
|
{"scroll", CampaignBonusType::SPELL_SCROLL},
|
||||||
|
{"primarySkill", CampaignBonusType::PRIMARY_SKILL},
|
||||||
|
{"secondarySkill", CampaignBonusType::SECONDARY_SKILL},
|
||||||
|
{"resource", CampaignBonusType::RESOURCE},
|
||||||
|
//{"prevHero", CScenarioTravel::STravelBonus::EBonusType::HEROES_FROM_PREVIOUS_SCENARIO},
|
||||||
|
//{"hero", CScenarioTravel::STravelBonus::EBonusType::HERO},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::string, ui32> primarySkillsMap = {
|
||||||
|
{"attack", 0},
|
||||||
|
{"defence", 8},
|
||||||
|
{"spellpower", 16},
|
||||||
|
{"knowledge", 24},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::string, ui16> heroSpecialMap = {
|
||||||
|
{"strongest", 0xFFFD},
|
||||||
|
{"generated", 0xFFFE},
|
||||||
|
{"random", 0xFFFF}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::string, ui8> resourceTypeMap = {
|
||||||
|
//FD - wood+ore
|
||||||
|
//FE - mercury+sulfur+crystal+gem
|
||||||
|
{"wood", 0},
|
||||||
|
{"mercury", 1},
|
||||||
|
{"ore", 2},
|
||||||
|
{"sulfur", 3},
|
||||||
|
{"crystal", 4},
|
||||||
|
{"gems", 5},
|
||||||
|
{"gold", 6},
|
||||||
|
{"common", 0xFD},
|
||||||
|
{"rare", 0xFE}
|
||||||
|
};
|
||||||
|
|
||||||
CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
|
CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
|
||||||
{
|
{
|
||||||
CampaignTravel ret;
|
CampaignTravel ret;
|
||||||
|
|
||||||
std::map<std::string, CampaignStartOptions> startOptionsMap = {
|
|
||||||
{"none", CampaignStartOptions::NONE},
|
|
||||||
{"bonus", CampaignStartOptions::START_BONUS},
|
|
||||||
{"crossover", CampaignStartOptions::HERO_CROSSOVER},
|
|
||||||
{"hero", CampaignStartOptions::HERO_OPTIONS}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<std::string, CampaignBonusType> bonusTypeMap = {
|
|
||||||
{"spell", CampaignBonusType::SPELL},
|
|
||||||
{"creature", CampaignBonusType::MONSTER},
|
|
||||||
{"building", CampaignBonusType::BUILDING},
|
|
||||||
{"artifact", CampaignBonusType::ARTIFACT},
|
|
||||||
{"scroll", CampaignBonusType::SPELL_SCROLL},
|
|
||||||
{"primarySkill", CampaignBonusType::PRIMARY_SKILL},
|
|
||||||
{"secondarySkill", CampaignBonusType::SECONDARY_SKILL},
|
|
||||||
{"resource", CampaignBonusType::RESOURCE},
|
|
||||||
//{"prevHero", CScenarioTravel::STravelBonus::EBonusType::HEROES_FROM_PREVIOUS_SCENARIO},
|
|
||||||
//{"hero", CScenarioTravel::STravelBonus::EBonusType::HERO},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<std::string, ui32> primarySkillsMap = {
|
|
||||||
{"attack", 0},
|
|
||||||
{"defence", 8},
|
|
||||||
{"spellpower", 16},
|
|
||||||
{"knowledge", 24},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<std::string, ui16> heroSpecialMap = {
|
|
||||||
{"strongest", 0xFFFD},
|
|
||||||
{"generated", 0xFFFE},
|
|
||||||
{"random", 0xFFFF}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<std::string, ui8> resourceTypeMap = {
|
|
||||||
//FD - wood+ore
|
|
||||||
//FE - mercury+sulfur+crystal+gem
|
|
||||||
{"wood", 0},
|
|
||||||
{"mercury", 1},
|
|
||||||
{"ore", 2},
|
|
||||||
{"sulfur", 3},
|
|
||||||
{"crystal", 4},
|
|
||||||
{"gems", 5},
|
|
||||||
{"gold", 6},
|
|
||||||
{"common", 0xFD},
|
|
||||||
{"rare", 0xFE}
|
|
||||||
};
|
|
||||||
|
|
||||||
for(auto & k : reader["heroKeeps"].Vector())
|
for(auto & k : reader["heroKeeps"].Vector())
|
||||||
{
|
{
|
||||||
@@ -278,7 +328,7 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
|
|||||||
logGlobal->warn("VCMP Loading: keepArtifacts contains unresolved identifier %s", k.String());
|
logGlobal->warn("VCMP Loading: keepArtifacts contains unresolved identifier %s", k.String());
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.startOptions = startOptionsMap[reader["startOptions"].String()];
|
ret.startOptions = startOptionsMap.at(reader["startOptions"].String());
|
||||||
switch(ret.startOptions)
|
switch(ret.startOptions)
|
||||||
{
|
{
|
||||||
case CampaignStartOptions::NONE:
|
case CampaignStartOptions::NONE:
|
||||||
@@ -290,11 +340,11 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
|
|||||||
for(auto & bjson : reader["bonuses"].Vector())
|
for(auto & bjson : reader["bonuses"].Vector())
|
||||||
{
|
{
|
||||||
CampaignBonus bonus;
|
CampaignBonus bonus;
|
||||||
bonus.type = bonusTypeMap[bjson["what"].String()];
|
bonus.type = bonusTypeMap.at(bjson["what"].String());
|
||||||
switch (bonus.type)
|
switch (bonus.type)
|
||||||
{
|
{
|
||||||
case CampaignBonusType::RESOURCE:
|
case CampaignBonusType::RESOURCE:
|
||||||
bonus.info1 = resourceTypeMap[bjson["type"].String()];
|
bonus.info1 = resourceTypeMap.at(bjson["type"].String());
|
||||||
bonus.info2 = bjson["amount"].Integer();
|
bonus.info2 = bjson["amount"].Integer();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -305,7 +355,7 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if(int heroId = heroSpecialMap[bjson["hero"].String()])
|
if(int heroId = heroSpecialMap.at(bjson["hero"].String()))
|
||||||
bonus.info1 = heroId;
|
bonus.info1 = heroId;
|
||||||
else
|
else
|
||||||
if(auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", bjson["hero"].String()))
|
if(auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", bjson["hero"].String()))
|
||||||
@@ -368,7 +418,7 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
|
|||||||
bonus.type = CampaignBonusType::HERO;
|
bonus.type = CampaignBonusType::HERO;
|
||||||
bonus.info1 = bjson["playerColor"].Integer(); //player color
|
bonus.info1 = bjson["playerColor"].Integer(); //player color
|
||||||
|
|
||||||
if(int heroId = heroSpecialMap[bjson["hero"].String()])
|
if(int heroId = heroSpecialMap.at(bjson["hero"].String()))
|
||||||
bonus.info2 = heroId;
|
bonus.info2 = heroId;
|
||||||
else
|
else
|
||||||
if (auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", bjson["hero"].String()))
|
if (auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", bjson["hero"].String()))
|
||||||
@@ -390,6 +440,109 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CampaignHandler::writeScenarioTravelToJson(JsonNode & node, const CampaignTravel & travel)
|
||||||
|
{
|
||||||
|
if(travel.whatHeroKeeps.experience)
|
||||||
|
node["heroKeeps"].Vector().push_back(JsonNode("experience"));
|
||||||
|
if(travel.whatHeroKeeps.primarySkills)
|
||||||
|
node["heroKeeps"].Vector().push_back(JsonNode("primarySkills"));
|
||||||
|
if(travel.whatHeroKeeps.secondarySkills)
|
||||||
|
node["heroKeeps"].Vector().push_back(JsonNode("secondarySkills"));
|
||||||
|
if(travel.whatHeroKeeps.spells)
|
||||||
|
node["heroKeeps"].Vector().push_back(JsonNode("spells"));
|
||||||
|
if(travel.whatHeroKeeps.artifacts)
|
||||||
|
node["heroKeeps"].Vector().push_back(JsonNode("artifacts"));
|
||||||
|
for(auto & c : travel.monstersKeptByHero)
|
||||||
|
node["keepCreatures"].Vector().push_back(JsonNode(CreatureID::encode(c)));
|
||||||
|
for(auto & a : travel.artifactsKeptByHero)
|
||||||
|
node["keepArtifacts"].Vector().push_back(JsonNode(ArtifactID::encode(a)));
|
||||||
|
node["startOptions"].String() = vstd::reverseMap(startOptionsMap)[travel.startOptions];
|
||||||
|
|
||||||
|
switch(travel.startOptions)
|
||||||
|
{
|
||||||
|
case CampaignStartOptions::NONE:
|
||||||
|
break;
|
||||||
|
case CampaignStartOptions::START_BONUS:
|
||||||
|
{
|
||||||
|
node["playerColor"].String() = PlayerColor::encode(travel.playerColor);
|
||||||
|
for(auto & bonus : travel.bonusesToChoose)
|
||||||
|
{
|
||||||
|
JsonNode bnode;
|
||||||
|
bnode["what"].String() = vstd::reverseMap(bonusTypeMap)[bonus.type];
|
||||||
|
switch (bonus.type)
|
||||||
|
{
|
||||||
|
case CampaignBonusType::RESOURCE:
|
||||||
|
bnode["type"].String() = vstd::reverseMap(resourceTypeMap)[bonus.info1];
|
||||||
|
bnode["amount"].Integer() = bonus.info2;
|
||||||
|
break;
|
||||||
|
case CampaignBonusType::BUILDING:
|
||||||
|
bnode["type"].String() = EBuildingType::names[bonus.info1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(vstd::contains(vstd::reverseMap(heroSpecialMap), bonus.info1))
|
||||||
|
bnode["hero"].String() = vstd::reverseMap(heroSpecialMap)[bonus.info1];
|
||||||
|
else
|
||||||
|
bnode["hero"].String() = HeroTypeID::encode(bonus.info1);
|
||||||
|
bnode["amount"].Integer() = bonus.info3;
|
||||||
|
switch(bonus.type)
|
||||||
|
{
|
||||||
|
case CampaignBonusType::SPELL:
|
||||||
|
bnode["type"].String() = SpellID::encode(bonus.info2);
|
||||||
|
break;
|
||||||
|
case CampaignBonusType::MONSTER:
|
||||||
|
bnode["type"].String() = CreatureID::encode(bonus.info2);
|
||||||
|
break;
|
||||||
|
case CampaignBonusType::SECONDARY_SKILL:
|
||||||
|
bnode["type"].String() = SecondarySkill::encode(bonus.info2);
|
||||||
|
break;
|
||||||
|
case CampaignBonusType::ARTIFACT:
|
||||||
|
bnode["type"].String() = ArtifactID::encode(bonus.info2);
|
||||||
|
break;
|
||||||
|
case CampaignBonusType::SPELL_SCROLL:
|
||||||
|
bnode["type"].String() = SpellID::encode(bonus.info2);
|
||||||
|
break;
|
||||||
|
case CampaignBonusType::PRIMARY_SKILL:
|
||||||
|
for(auto & ps : primarySkillsMap)
|
||||||
|
bnode[ps.first].Integer() = (bonus.info2 >> ps.second) & 0xff;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bnode["type"].Integer() = bonus.info2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node["bonuses"].Vector().push_back(bnode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CampaignStartOptions::HERO_CROSSOVER:
|
||||||
|
{
|
||||||
|
for(auto & bonus : travel.bonusesToChoose)
|
||||||
|
{
|
||||||
|
JsonNode bnode;
|
||||||
|
bnode["playerColor"].Integer() = bonus.info1;
|
||||||
|
bnode["scenario"].Integer() = bonus.info2;
|
||||||
|
node["bonuses"].Vector().push_back(bnode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CampaignStartOptions::HERO_OPTIONS:
|
||||||
|
{
|
||||||
|
for(auto & bonus : travel.bonusesToChoose)
|
||||||
|
{
|
||||||
|
JsonNode bnode;
|
||||||
|
bnode["playerColor"].Integer() = bonus.info1;
|
||||||
|
|
||||||
|
if(vstd::contains(vstd::reverseMap(heroSpecialMap), bonus.info2))
|
||||||
|
bnode["hero"].String() = vstd::reverseMap(heroSpecialMap)[bonus.info2];
|
||||||
|
else
|
||||||
|
bnode["hero"].String() = HeroTypeID::encode(bonus.info2);
|
||||||
|
|
||||||
|
node["bonuses"].Vector().push_back(bnode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CampaignHandler::readHeaderFromMemory( CampaignHeader & ret, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding )
|
void CampaignHandler::readHeaderFromMemory( CampaignHeader & ret, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ class DLL_LINKAGE CampaignHandler
|
|||||||
static CampaignScenario readScenarioFromJson(JsonNode & reader);
|
static CampaignScenario readScenarioFromJson(JsonNode & reader);
|
||||||
static CampaignTravel readScenarioTravelFromJson(JsonNode & reader);
|
static CampaignTravel readScenarioTravelFromJson(JsonNode & reader);
|
||||||
|
|
||||||
|
//writer for VCMI campaigns (*.vcmp)
|
||||||
|
static void writeScenarioTravelToJson(JsonNode & node, const CampaignTravel & travel);
|
||||||
|
|
||||||
//parsers for original H3C campaigns
|
//parsers for original H3C campaigns
|
||||||
static void readHeaderFromMemory(CampaignHeader & target, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding);
|
static void readHeaderFromMemory(CampaignHeader & target, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding);
|
||||||
static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, CampaignHeader & header);
|
static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, CampaignHeader & header);
|
||||||
@@ -43,6 +46,10 @@ public:
|
|||||||
static std::unique_ptr<Campaign> getHeader( const std::string & name); //name - name of appropriate file
|
static std::unique_ptr<Campaign> getHeader( const std::string & name); //name - name of appropriate file
|
||||||
|
|
||||||
static std::shared_ptr<CampaignState> getCampaign(const std::string & name); //name - name of appropriate file
|
static std::shared_ptr<CampaignState> getCampaign(const std::string & name); //name - name of appropriate file
|
||||||
|
|
||||||
|
//writer for VCMI campaigns (*.vcmp)
|
||||||
|
static JsonNode writeHeaderToJson(CampaignHeader & header);
|
||||||
|
static JsonNode writeScenarioToJson(const CampaignScenario & scenario);
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|||||||
@@ -45,6 +45,22 @@ CampaignRegions::RegionDescription CampaignRegions::RegionDescription::fromJson(
|
|||||||
return rd;
|
return rd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonNode CampaignRegions::RegionDescription::toJson(CampaignRegions::RegionDescription & rd)
|
||||||
|
{
|
||||||
|
JsonNode node;
|
||||||
|
node["infix"].String() = rd.infix;
|
||||||
|
node["x"].Float() = rd.pos.x;
|
||||||
|
node["y"].Float() = rd.pos.y;
|
||||||
|
if(rd.labelPos != std::nullopt)
|
||||||
|
{
|
||||||
|
node["labelPos"]["x"].Float() = (*rd.labelPos).x;
|
||||||
|
node["labelPos"]["y"].Float() = (*rd.labelPos).y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
node["labelPos"].clear();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
CampaignRegions CampaignRegions::fromJson(const JsonNode & node)
|
CampaignRegions CampaignRegions::fromJson(const JsonNode & node)
|
||||||
{
|
{
|
||||||
CampaignRegions cr;
|
CampaignRegions cr;
|
||||||
@@ -59,6 +75,25 @@ CampaignRegions CampaignRegions::fromJson(const JsonNode & node)
|
|||||||
return cr;
|
return cr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonNode CampaignRegions::toJson(CampaignRegions cr)
|
||||||
|
{
|
||||||
|
JsonNode node;
|
||||||
|
node["prefix"].String() = cr.campPrefix;
|
||||||
|
node["colorSuffixLength"].Float() = cr.colorSuffixLength;
|
||||||
|
if(!cr.campSuffix.size())
|
||||||
|
node["suffix"].clear();
|
||||||
|
else
|
||||||
|
node["suffix"].Vector() = JsonVector{ JsonNode(cr.campSuffix[0]), JsonNode(cr.campSuffix[1]), JsonNode(cr.campSuffix[2]) };
|
||||||
|
if(cr.campBackground.empty())
|
||||||
|
node["background"].clear();
|
||||||
|
else
|
||||||
|
node["background"].String() = cr.campBackground;
|
||||||
|
node["desc"].Vector() = JsonVector();
|
||||||
|
for(auto & region : cr.regions)
|
||||||
|
node["desc"].Vector().push_back(CampaignRegions::RegionDescription::toJson(region));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
CampaignRegions CampaignRegions::getLegacy(int campId)
|
CampaignRegions CampaignRegions::getLegacy(int campId)
|
||||||
{
|
{
|
||||||
static std::vector<CampaignRegions> campDescriptions;
|
static std::vector<CampaignRegions> campDescriptions;
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class DLL_LINKAGE CampaignRegions
|
|||||||
}
|
}
|
||||||
|
|
||||||
static CampaignRegions::RegionDescription fromJson(const JsonNode & node);
|
static CampaignRegions::RegionDescription fromJson(const JsonNode & node);
|
||||||
|
static JsonNode toJson(CampaignRegions::RegionDescription & rd);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<RegionDescription> regions;
|
std::vector<RegionDescription> regions;
|
||||||
@@ -86,6 +87,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static CampaignRegions fromJson(const JsonNode & node);
|
static CampaignRegions fromJson(const JsonNode & node);
|
||||||
|
static JsonNode toJson(CampaignRegions cr);
|
||||||
static CampaignRegions getLegacy(int campId);
|
static CampaignRegions getLegacy(int campId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ TModID CModHandler::findResourceOrigin(const ResourcePath & name) const
|
|||||||
return "core";
|
return "core";
|
||||||
|
|
||||||
if(CResourceHandler::get("mapEditor")->existsResource(name))
|
if(CResourceHandler::get("mapEditor")->existsResource(name))
|
||||||
return "core"; // Workaround for loading maps via map editor
|
return "mapEditor"; // Workaround for loading maps via map editor
|
||||||
}
|
}
|
||||||
catch( const std::out_of_range & e)
|
catch( const std::out_of_range & e)
|
||||||
{
|
{
|
||||||
@@ -189,6 +189,8 @@ std::string CModHandler::getModLanguage(const TModID& modId) const
|
|||||||
return VLC->generaltexth->getInstalledLanguage();
|
return VLC->generaltexth->getInstalledLanguage();
|
||||||
if(modId == "map")
|
if(modId == "map")
|
||||||
return VLC->generaltexth->getPreferredLanguage();
|
return VLC->generaltexth->getPreferredLanguage();
|
||||||
|
if(modId == "mapEditor")
|
||||||
|
return VLC->generaltexth->getPreferredLanguage();
|
||||||
return getModInfo(modId).getBaseLanguage();
|
return getModInfo(modId).getBaseLanguage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@
|
|||||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||||
#include "../lib/CConfigHandler.h"
|
#include "../lib/CConfigHandler.h"
|
||||||
#include "../lib/filesystem/Filesystem.h"
|
#include "../lib/filesystem/Filesystem.h"
|
||||||
|
#include "../lib/filesystem/CMemoryBuffer.h"
|
||||||
#include "../lib/GameConstants.h"
|
#include "../lib/GameConstants.h"
|
||||||
|
#include "../lib/campaign/CampaignHandler.h"
|
||||||
#include "../lib/mapObjectConstructors/AObjectTypeHandler.h"
|
#include "../lib/mapObjectConstructors/AObjectTypeHandler.h"
|
||||||
#include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
|
#include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
|
||||||
#include "../lib/mapObjects/ObjectTemplate.h"
|
#include "../lib/mapObjects/ObjectTemplate.h"
|
||||||
@@ -32,6 +34,7 @@
|
|||||||
#include "../lib/mapping/CMap.h"
|
#include "../lib/mapping/CMap.h"
|
||||||
#include "../lib/mapping/CMapEditManager.h"
|
#include "../lib/mapping/CMapEditManager.h"
|
||||||
#include "../lib/mapping/MapFormat.h"
|
#include "../lib/mapping/MapFormat.h"
|
||||||
|
#include "../lib/mapping/MapFormatJson.h"
|
||||||
#include "../lib/modding/ModIncompatibility.h"
|
#include "../lib/modding/ModIncompatibility.h"
|
||||||
#include "../lib/RoadHandler.h"
|
#include "../lib/RoadHandler.h"
|
||||||
#include "../lib/RiverHandler.h"
|
#include "../lib/RiverHandler.h"
|
||||||
@@ -398,6 +401,27 @@ std::unique_ptr<CMap> MainWindow::openMapInternal(const QString & filenameSelect
|
|||||||
throw std::runtime_error("Corrupted map");
|
throw std::runtime_error("Corrupted map");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CampaignState> MainWindow::openCampaignInternal(const QString & filenameSelect)
|
||||||
|
{
|
||||||
|
QFileInfo fi(filenameSelect);
|
||||||
|
std::string fname = fi.fileName().toStdString();
|
||||||
|
std::string fdir = fi.dir().path().toStdString();
|
||||||
|
|
||||||
|
ResourcePath resId("MAPEDITOR/" + fname, EResType::CAMPAIGN);
|
||||||
|
|
||||||
|
//addFilesystem takes care about memory deallocation if case of failure, no memory leak here
|
||||||
|
auto * mapEditorFilesystem = new CFilesystemLoader("MAPEDITOR/", fdir, 0);
|
||||||
|
CResourceHandler::removeFilesystem("local", "mapEditor");
|
||||||
|
CResourceHandler::addFilesystem("local", "mapEditor", mapEditorFilesystem);
|
||||||
|
|
||||||
|
if(!CResourceHandler::get("mapEditor")->existsResource(resId))
|
||||||
|
throw std::runtime_error("Cannot open campaign from this folder");
|
||||||
|
if(auto campaign = CampaignHandler::getCampaign(resId.getName()))
|
||||||
|
return campaign;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Corrupted campaign");
|
||||||
|
}
|
||||||
|
|
||||||
bool MainWindow::openMap(const QString & filenameSelect)
|
bool MainWindow::openMap(const QString & filenameSelect)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -1373,6 +1397,53 @@ void MainWindow::on_actionh3m_converter_triggered()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionh3c_converter_triggered()
|
||||||
|
{
|
||||||
|
auto campaignFile = QFileDialog::getOpenFileName(this, tr("Select campaign to convert"),
|
||||||
|
QString::fromStdString(VCMIDirs::get().userDataPath().make_preferred().string()),
|
||||||
|
tr("HoMM3 campaigns (*.h3c)"));
|
||||||
|
if(campaignFile.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto campaignFileDest = QFileDialog::getSaveFileName(this, tr("Select destination file"),
|
||||||
|
QString::fromStdString(VCMIDirs::get().userDataPath().make_preferred().string()),
|
||||||
|
tr("VCMI campaigns (*.vcmp)"));
|
||||||
|
if(campaignFileDest.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFileInfo fileInfo(campaignFileDest);
|
||||||
|
if(fileInfo.suffix().toLower() != "vcmp")
|
||||||
|
campaignFileDest += ".vcmp";
|
||||||
|
auto campaign = openCampaignInternal(campaignFile);
|
||||||
|
|
||||||
|
auto jsonCampaign = CampaignHandler::writeHeaderToJson(*campaign);
|
||||||
|
|
||||||
|
std::shared_ptr<CIOApi> io(new CDefaultIOApi());
|
||||||
|
auto saver = std::make_shared<CZipSaver>(io, campaignFileDest.toStdString());
|
||||||
|
for(auto & scenario : campaign->allScenarios())
|
||||||
|
{
|
||||||
|
CMapService mapService;
|
||||||
|
auto map = campaign->getMap(scenario, nullptr);
|
||||||
|
controller.repairMap(map.get());
|
||||||
|
CMemoryBuffer serializeBuffer;
|
||||||
|
{
|
||||||
|
CMapSaverJson jsonSaver(&serializeBuffer);
|
||||||
|
jsonSaver.saveMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mapName = boost::algorithm::to_lower_copy(campaign->scenario(scenario).mapName);
|
||||||
|
mapName = boost::replace_all_copy(mapName, ".h3m", std::string("")) + ".vmap";
|
||||||
|
|
||||||
|
auto stream = saver->addFile(mapName);
|
||||||
|
stream->write(reinterpret_cast<const ui8 *>(serializeBuffer.getBuffer().data()), serializeBuffer.getSize());
|
||||||
|
|
||||||
|
jsonCampaign["scenarios"].Vector().push_back(CampaignHandler::writeScenarioToJson(campaign->scenario(scenario)));
|
||||||
|
jsonCampaign["scenarios"].Vector().back()["map"].String() = mapName;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto jsonCampaignStr = jsonCampaign.toString();
|
||||||
|
saver->addFile("header.json")->write(reinterpret_cast<const ui8 *>(jsonCampaignStr.data()), jsonCampaignStr.length());
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionLock_triggered()
|
void MainWindow::on_actionLock_triggered()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class ObjectBrowserProxyModel;
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
class CMap;
|
class CMap;
|
||||||
|
class CampaignState;
|
||||||
class CGObjectInstance;
|
class CGObjectInstance;
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ class MainWindow : public QMainWindow
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::unique_ptr<CMap> openMapInternal(const QString &);
|
std::unique_ptr<CMap> openMapInternal(const QString &);
|
||||||
|
std::shared_ptr<CampaignState> openCampaignInternal(const QString &);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
@@ -118,6 +120,8 @@ private slots:
|
|||||||
|
|
||||||
void on_actionh3m_converter_triggered();
|
void on_actionh3m_converter_triggered();
|
||||||
|
|
||||||
|
void on_actionh3c_converter_triggered();
|
||||||
|
|
||||||
void on_actionLock_triggered();
|
void on_actionLock_triggered();
|
||||||
|
|
||||||
void on_actionUnlock_triggered();
|
void on_actionUnlock_triggered();
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
<addaction name="actionSave_as"/>
|
<addaction name="actionSave_as"/>
|
||||||
<addaction name="actionExport"/>
|
<addaction name="actionExport"/>
|
||||||
<addaction name="actionh3m_converter"/>
|
<addaction name="actionh3m_converter"/>
|
||||||
|
<addaction name="actionh3c_converter"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuMap">
|
<widget class="QMenu" name="menuMap">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@@ -1352,6 +1353,14 @@
|
|||||||
<string>h3m converter</string>
|
<string>h3m converter</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionh3c_converter">
|
||||||
|
<property name="text">
|
||||||
|
<string>h3c converter</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>h3c converter</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionLock">
|
<action name="actionLock">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Lock</string>
|
<string>Lock</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user