1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-20 20:23:03 +02:00

- it is possible to edit data of another mod or H3 data via mods

- mods can access only ID's from dependenies, virtual "core" mod and itself (optional for some mods compatibility)
- metadata field for JsonNode, used to track source mod
- moved wog creatures into wog mod
- (linux) convertMP3 option for vcmibuilder for systems where SDL_Mixer can't play mp3's
This commit is contained in:
Ivan Savenko 2013-04-25 14:03:35 +00:00
parent be6aff5173
commit 8273f323b1
24 changed files with 321 additions and 98 deletions

View File

@ -0,0 +1,38 @@
{
"core:castle" :
{
"commander" : "paladin1"
},
"core:conflux" :
{
"commander" : "astralSpirit1"
},
"core:dungeon" :
{
"commander" : "brute1"
},
"core:fortress" :
{
"commander" : "shaman1"
},
"core:inferno" :
{
"commander" : "succubus1"
},
"core:necropolis" :
{
"commander" : "soulEater1"
},
"core:rampart" :
{
"commander" : "hierophant1"
},
"core:stronghold" :
{
"commander" : "ogreLeader1"
},
"core:tower" :
{
"commander" : "templeGuardian1"
}
}

View File

@ -1,6 +1,10 @@
{ {
"filesystem": "filesystem":
{ {
"CONFIG/" :
[
{ "type" : "dir", "path" : "/Config"}
],
"DATA/" : "DATA/" :
[ [
{"type" : "lod", "path" : "/Data/hmm35wog.pac"}, {"type" : "lod", "path" : "/Data/hmm35wog.pac"},
@ -34,5 +38,15 @@
}, },
"name" : "In The Wake of Gods", "name" : "In The Wake of Gods",
"description" : "Unnofficial addon for Heroes of Might and Magic III" "description" : "Unnofficial addon for Heroes of Might and Magic III",
"creatures" :
[
"config/wog/creatures.json"
],
"factions" :
[
"config/wog/factions.json"
]
} }

View File

@ -4417,6 +4417,12 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
bonusValue = 0; bonusValue = 0;
} }
} }
else
{
baseType = CComponent::artifact;
type = art->artType->iconIndex;
bonusValue = 0;
}
if (locked) // Locks should appear as empty. if (locked) // Locks should appear as empty.
hoverText = CGI->generaltexth->allTexts[507]; hoverText = CGI->generaltexth->allTexts[507];

View File

@ -4,7 +4,7 @@
"index" : 0, "index" : 0,
"nativeTerrain": "grass", "nativeTerrain": "grass",
"alignment" : "good", "alignment" : "good",
"commander" : "paladin1", "commander" : "zealot",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASCAS", "120px" : "TPCASCAS",

View File

@ -4,7 +4,7 @@
"index" : 8, "index" : 8,
"nativeTerrain": "grass", "nativeTerrain": "grass",
"alignment" : "neutral", "alignment" : "neutral",
"commander" : "astralSpirit1", "commander" : "iceElemental",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASELE", "120px" : "TPCASELE",

View File

@ -4,7 +4,7 @@
"index" : 5, "index" : 5,
"nativeTerrain": "subterra", "nativeTerrain": "subterra",
"alignment" : "evil", "alignment" : "evil",
"commander" : "brute1", "commander" : "medusaQueen",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASDUN", "120px" : "TPCASDUN",

View File

@ -4,7 +4,7 @@
"index" : 7, "index" : 7,
"nativeTerrain": "swamp", "nativeTerrain": "swamp",
"alignment" : "neutral", "alignment" : "neutral",
"commander" : "shaman1", "commander" : "lizardWarrior",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASFOR", "120px" : "TPCASFOR",

View File

@ -4,7 +4,7 @@
"index" : 3, "index" : 3,
"nativeTerrain": "lava", "nativeTerrain": "lava",
"alignment" : "evil", "alignment" : "evil",
"commander" : "succubus1", "commander" : "magog",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASINF", "120px" : "TPCASINF",

View File

@ -4,7 +4,7 @@
"index" : 4, "index" : 4,
"nativeTerrain": "dirt", "nativeTerrain": "dirt",
"alignment" : "evil", "alignment" : "evil",
"commander" : "soulEater1", "commander" : "powerLich",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASNEC", "120px" : "TPCASNEC",

View File

@ -4,7 +4,7 @@
"index" : 1, "index" : 1,
"nativeTerrain": "grass", "nativeTerrain": "grass",
"alignment" : "good", "alignment" : "good",
"commander" : "hierophant1", "commander" : "grandElf",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASRAM", "120px" : "TPCASRAM",

View File

@ -4,7 +4,7 @@
"index" : 6, "index" : 6,
"nativeTerrain": "rough", "nativeTerrain": "rough",
"alignment" : "neutral", "alignment" : "neutral",
"commander" : "ogreLeader1", "commander" : "orcChieftain",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASSTR", "120px" : "TPCASSTR",

View File

@ -4,7 +4,7 @@
"index" : 2, "index" : 2,
"nativeTerrain" : "snow", "nativeTerrain" : "snow",
"alignment" : "good", "alignment" : "good",
"commander" : "templeGuardian1", "commander" : "archMage",
"creatureBackground" : "creatureBackground" :
{ {
"120px" : "TPCASTOW", "120px" : "TPCASTOW",

View File

@ -28,8 +28,7 @@
"config/creatures/conflux.json", "config/creatures/conflux.json",
"config/creatures/neutral.json", "config/creatures/neutral.json",
"config/creatures/special.json", "config/creatures/special.json"
"config/creatures/wog.json"
], ],
"heroes" : "heroes" :

View File

@ -366,7 +366,7 @@ void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
art->constituents.reset(new std::vector<CArtifact *>()); art->constituents.reset(new std::vector<CArtifact *>());
BOOST_FOREACH (auto component, node["components"].Vector()) BOOST_FOREACH (auto component, node["components"].Vector())
{ {
VLC->modh->identifiers.requestIdentifier("artifact." + component.String(), [=](si32 id) VLC->modh->identifiers.requestIdentifier("artifact", component, [=](si32 id)
{ {
// when this code is called both combinational art as well as component are loaded // when this code is called both combinational art as well as component are loaded
// so it is safe to access any of them // so it is safe to access any of them

View File

@ -179,7 +179,10 @@ CCreatureHandler::CCreatureHandler()
void CCreatureHandler::loadCommanders() void CCreatureHandler::loadCommanders()
{ {
const JsonNode config(ResourceID("config/commanders.json")); JsonNode data(ResourceID("config/commanders.json"));
data.setMeta("core"); // assume that commanders are in core mod (for proper bonuses resolution)
const JsonNode & config = data; // switch to const data accessors
BOOST_FOREACH (auto bonus, config["bonusPerLevel"].Vector()) BOOST_FOREACH (auto bonus, config["bonusPerLevel"].Vector())
{ {
@ -627,14 +630,14 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
} }
} }
VLC->modh->identifiers.requestIdentifier(std::string("faction.") + config["faction"].String(), [=](si32 faction) VLC->modh->identifiers.requestIdentifier("faction", config["faction"], [=](si32 faction)
{ {
creature->faction = faction; creature->faction = faction;
}); });
BOOST_FOREACH(const JsonNode &value, config["upgrades"].Vector()) BOOST_FOREACH(const JsonNode &value, config["upgrades"].Vector())
{ {
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + value.String(), [=](si32 identifier) VLC->modh->identifiers.requestIdentifier("creature", value, [=](si32 identifier)
{ {
creature->upgrades.insert(CreatureID(identifier)); creature->upgrades.insert(CreatureID(identifier));
}); });

View File

@ -105,14 +105,14 @@ CHeroClass *CHeroClassHandler::loadFromJson(const JsonNode & node)
{ {
int value = tavern.second.Float(); int value = tavern.second.Float();
VLC->modh->identifiers.requestIdentifier("faction." + tavern.first, VLC->modh->identifiers.requestIdentifier(tavern.second.meta, "faction", tavern.first,
[=](si32 factionID) [=](si32 factionID)
{ {
heroClass->selectionProbability[factionID] = value; heroClass->selectionProbability[factionID] = value;
}); });
} }
VLC->modh->identifiers.requestIdentifier("faction." + node["faction"].String(), VLC->modh->identifiers.requestIdentifier("faction", node["faction"],
[=](si32 factionID) [=](si32 factionID)
{ {
heroClass->faction = factionID; heroClass->faction = factionID;
@ -237,7 +237,7 @@ CHero * CHeroHandler::loadFromJson(const JsonNode & node)
loadHeroSkills(hero, node); loadHeroSkills(hero, node);
loadHeroSpecialty(hero, node); loadHeroSpecialty(hero, node);
VLC->modh->identifiers.requestIdentifier("heroClass." + node["class"].String(), VLC->modh->identifiers.requestIdentifier("heroClass", node["class"],
[=](si32 classID) [=](si32 classID)
{ {
hero->heroClass = classes.heroClasses[classID]; hero->heroClass = classes.heroClasses[classID];
@ -261,7 +261,7 @@ void CHeroHandler::loadHeroArmy(CHero * hero, const JsonNode & node)
assert(hero->initialArmy[i].minAmount <= hero->initialArmy[i].maxAmount); assert(hero->initialArmy[i].minAmount <= hero->initialArmy[i].maxAmount);
VLC->modh->identifiers.requestIdentifier("creature." + source["creature"].String(), [=](si32 creature) VLC->modh->identifiers.requestIdentifier("creature", source["creature"], [=](si32 creature)
{ {
hero->initialArmy[i].creature = CreatureID(creature); hero->initialArmy[i].creature = CreatureID(creature);
}); });
@ -278,7 +278,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
size_t currentIndex = hero->secSkillsInit.size(); size_t currentIndex = hero->secSkillsInit.size();
hero->secSkillsInit.push_back(std::make_pair(SecondarySkill(-1), skillLevel)); hero->secSkillsInit.push_back(std::make_pair(SecondarySkill(-1), skillLevel));
VLC->modh->identifiers.requestIdentifier("skill." + set["skill"].String(), [=](si32 id) VLC->modh->identifiers.requestIdentifier("skill", set["skill"], [=](si32 id)
{ {
hero->secSkillsInit[currentIndex].first = SecondarySkill(id); hero->secSkillsInit[currentIndex].first = SecondarySkill(id);
}); });
@ -300,7 +300,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
} }
else else
{ {
VLC->modh->identifiers.requestIdentifier("spell." + spell.String(), VLC->modh->identifiers.requestIdentifier("spell", spell,
[=](si32 spellID) [=](si32 spellID)
{ {
hero->spells.insert(SpellID(spellID)); hero->spells.insert(SpellID(spellID));

View File

@ -44,82 +44,142 @@ void CIdentifierStorage::checkIdentifier(std::string & ID)
} }
} }
void CIdentifierStorage::requestIdentifier(std::string name, const boost::function<void(si32)> & callback) CIdentifierStorage::ObjectCallback::ObjectCallback(std::string localScope, std::string remoteScope, std::string type,
std::string name, const boost::function<void(si32)> & callback):
localScope(localScope),
remoteScope(remoteScope),
type(type),
name(name),
callback(callback)
{}
static std::pair<std::string, std::string> splitString(std::string input, char separator)
{ {
checkIdentifier(name); std::pair<std::string, std::string> ret;
size_t splitPos = input.find(separator);
// old version with immediate callback posibility. Can't be used for some cases if (splitPos == std::string::npos)
/* auto iter = registeredObjects.find(name); {
ret.first.clear();
if (iter != registeredObjects.end()) ret.second = input;
callback(iter->second); //already registered - trigger callback immediately }
else else
{ {
if(boost::algorithm::starts_with(name, "primSkill.")) ret.first = input.substr(0, splitPos);
logGlobal->warnStream() << "incorrect primSkill name requested"; ret.second = input.substr(splitPos + 1);
}
return ret;
}
missingObjects[name].push_back(callback); // queue callback void CIdentifierStorage::requestIdentifier(ObjectCallback callback)
}*/ {
checkIdentifier(callback.type);
checkIdentifier(callback.name);
missingObjects[name].push_back(callback); // queue callback assert(!callback.localScope.empty());
scheduledRequests.push_back(callback);
}
void CIdentifierStorage::requestIdentifier(std::string scope, std::string type, std::string name, const boost::function<void(si32)> & callback)
{
auto pair = splitString(name, ':'); // remoteScope:name
requestIdentifier(ObjectCallback(scope, pair.first, type, pair.second, callback));
}
void CIdentifierStorage::requestIdentifier(std::string type, const JsonNode & name, const boost::function<void(si32)> & callback)
{
auto pair = splitString(name.String(), ':'); // remoteScope:name
requestIdentifier(ObjectCallback(name.meta, pair.first, type, pair.second, callback));
}
void CIdentifierStorage::requestIdentifier(const JsonNode & name, const boost::function<void(si32)> & callback)
{
auto pair = splitString(name.String(), ':'); // remoteScope:<type.name>
auto pair2 = splitString(pair.second, '.'); // type.name
requestIdentifier(ObjectCallback(name.meta, pair.first, pair2.first, pair2.second, callback));
} }
void CIdentifierStorage::registerObject(std::string scope, std::string type, std::string name, si32 identifier) void CIdentifierStorage::registerObject(std::string scope, std::string type, std::string name, si32 identifier)
{ {
//TODO: use scope ObjectData data;
data.scope = scope;
data.id = identifier;
std::string fullID = type + '.' + name; std::string fullID = type + '.' + name;
checkIdentifier(fullID); checkIdentifier(fullID);
// do not allow to register same object twice registeredObjects.insert(std::make_pair(fullID, data));
assert(registeredObjects.find(fullID) == registeredObjects.end()); }
registeredObjects[fullID] = identifier; bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
{
std::set<std::string> allowedScopes;
// old version with immediate callback posibility. Can't be used for some cases if (request.remoteScope.empty())
/*auto iter = missingObjects.find(fullID);
if (iter != missingObjects.end())
{ {
//call all awaiting callbacks // normally ID's from all required mods, own mod and virtual "core" mod are allowed
BOOST_FOREACH(auto function, iter->second) if (request.localScope != "core")
{ allowedScopes = VLC->modh->getModData(request.localScope).dependencies;
function(identifier);
allowedScopes.insert(request.localScope);
allowedScopes.insert("core");
} }
missingObjects.erase(iter); else
}*/ {
// //...unless destination mod was specified explicitly
allowedScopes.insert(request.remoteScope);
}
std::string fullID = request.type + '.' + request.name;
auto entries = registeredObjects.equal_range(fullID);
if (entries.first != entries.second)
{
for (auto it = entries.first; it != entries.second; it++)
{
if (vstd::contains(allowedScopes, it->second.scope))
{
request.callback(it->second.id);
return true;
}
}
// error found. Try to generate some debug info
logGlobal->errorStream() << "Unknown identifier " << request.type << "." << request.name << " from mod " << request.localScope;
for (auto it = entries.first; it != entries.second; it++)
{
logGlobal->errorStream() << "\tID is available in mod " << it->second.scope;
}
// temporary code to smooth 0.92->0.93 transition
request.callback(entries.first->second.id);
return true;
}
return false;
} }
void CIdentifierStorage::finalize() void CIdentifierStorage::finalize()
{ {
for (auto it = missingObjects.begin(); it!= missingObjects.end();) bool errorsFound = false;
BOOST_FOREACH(const ObjectCallback & request, scheduledRequests)
{ {
auto object = registeredObjects.find(it->first); errorsFound |= !resolveIdentifier(request);
if (object != registeredObjects.end())
{
BOOST_FOREACH(auto function, it->second)
{
function(object->second);
}
it = missingObjects.erase(it);
}
else
it++;
} }
// print list of missing objects and crash if (errorsFound)
// in future should try to do some cleanup (like returning all id's as 0)
if (!missingObjects.empty())
{ {
BOOST_FOREACH(auto object, missingObjects)
{
logGlobal->errorStream() << "Error: object " << object.first << " was not found!";
}
BOOST_FOREACH(auto object, registeredObjects) BOOST_FOREACH(auto object, registeredObjects)
{ {
logGlobal->traceStream() << object.first << " -> " << object.second; logGlobal->traceStream() << object.first << " -> " << object.second.id;
} }
logGlobal->errorStream() << "All known identifiers were dumped into log file"; logGlobal->errorStream() << "All known identifiers were dumped into log file";
} }
assert(missingObjects.empty()); assert(errorsFound == false);
} }
CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, size_t size, std::string objectName): CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, size_t size, std::string objectName):
@ -127,11 +187,16 @@ CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler,
objectName(objectName), objectName(objectName),
originalData(handler->loadLegacyData(size)) originalData(handler->loadLegacyData(size))
{ {
BOOST_FOREACH(auto & node, originalData)
{
node.setMeta("core");
}
} }
void CContentHandler::ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList) void CContentHandler::ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList)
{ {
JsonNode data = JsonUtils::assembleFromFiles(fileList); JsonNode data = JsonUtils::assembleFromFiles(fileList);
data.setMeta(modName);
ModInfo & modInfo = modData[modName]; ModInfo & modInfo = modData[modName];
@ -425,6 +490,13 @@ std::vector<std::string> CModHandler::getActiveMods()
return activeMods; return activeMods;
} }
CModInfo & CModHandler::getModData(TModID modId)
{
CModInfo & mod = allMods.at(modId);
assert(vstd::contains(activeMods, modId)); // not really necessary but won't hurt
return mod;
}
template<typename Handler> template<typename Handler>
void CModHandler::handleData(Handler handler, const JsonNode & source, std::string listName, std::string schemaName) void CModHandler::handleData(Handler handler, const JsonNode & source, std::string listName, std::string schemaName)
{ {
@ -447,7 +519,7 @@ void CModHandler::loadGameContent()
CContentHandler content; CContentHandler content;
logGlobal->infoStream() << "\tInitializing content hander: " << timer.getDiff() << " ms"; logGlobal->infoStream() << "\tInitializing content hander: " << timer.getDiff() << " ms";
// first - load virtual "core" mod tht contains all data // first - load virtual "core" mod that contains all data
// TODO? move all data into real mods? RoE, AB, SoD, WoG // TODO? move all data into real mods? RoE, AB, SoD, WoG
content.preloadModData("core", JsonNode(ResourceID("config/gameConfig.json"))); content.preloadModData("core", JsonNode(ResourceID("config/gameConfig.json")));
logGlobal->infoStream() << "\tParsing original game data: " << timer.getDiff() << " ms"; logGlobal->infoStream() << "\tParsing original game data: " << timer.getDiff() << " ms";
@ -475,12 +547,11 @@ void CModHandler::loadGameContent()
} }
logGlobal->infoStream() << "\tLoading mod data: " << timer.getDiff() << "ms"; logGlobal->infoStream() << "\tLoading mod data: " << timer.getDiff() << "ms";
logGlobal->infoStream() << "\tDone loading data";
VLC->creh->loadCrExpBon(); VLC->creh->loadCrExpBon();
VLC->creh->buildBonusTreeForTiers(); //do that after all new creatures are loaded VLC->creh->buildBonusTreeForTiers(); //do that after all new creatures are loaded
identifiers.finalize(); identifiers.finalize();
logGlobal->infoStream() << "\tResolving identifiers: " << timer.getDiff() << " ms";
logGlobal->infoStream() << "\tAll game content loaded in " << totalTime.getDiff() << " ms"; logGlobal->infoStream() << "\tAll game content loaded in " << totalTime.getDiff() << " ms";
} }

View File

@ -25,15 +25,38 @@ class IHandlerBase;
/// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf" /// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
class CIdentifierStorage class CIdentifierStorage
{ {
std::map<std::string, si32 > registeredObjects; struct ObjectCallback // entry created on ID request
std::map<std::string, std::vector<boost::function<void(si32)> > > missingObjects; {
std::string localScope; /// scope from which this ID was requested
std::string remoteScope; /// scope in which this object must be found
std::string type; /// type, e.g. creature, faction, hero, etc
std::string name; /// string ID
boost::function<void(si32)> callback;
ObjectCallback(std::string localScope, std::string remoteScope, std::string type, std::string name, const boost::function<void(si32)> & callback);
};
struct ObjectData // entry created on ID registration
{
si32 id;
std::string scope; /// scope in which this ID located
};
std::multimap<std::string, ObjectData > registeredObjects;
std::vector<ObjectCallback> scheduledRequests;
/// Check if identifier can be valid (camelCase, point as separator) /// Check if identifier can be valid (camelCase, point as separator)
void checkIdentifier(std::string & ID); void checkIdentifier(std::string & ID);
void requestIdentifier(ObjectCallback callback);
bool resolveIdentifier(const ObjectCallback & callback);
public: public:
/// request identifier for specific object name. If ID is not yet resolved callback will be queued /// request identifier for specific object name. If ID is not yet resolved callback will be queued
/// and will be called later /// and will be called later
void requestIdentifier(std::string name, const boost::function<void(si32)> & callback); void requestIdentifier(std::string scope, std::string type, std::string name, const boost::function<void(si32)> & callback);
void requestIdentifier(std::string type, const JsonNode & name, const boost::function<void(si32)> & callback);
void requestIdentifier(const JsonNode & name, const boost::function<void(si32)> & callback);
/// registers new object, calls all associated callbacks /// registers new object, calls all associated callbacks
void registerObject(std::string scope, std::string type, std::string name, si32 identifier); void registerObject(std::string scope, std::string type, std::string name, si32 identifier);
@ -143,6 +166,8 @@ public:
/// returns list of mods that should be active with order in which they shoud be loaded /// returns list of mods that should be active with order in which they shoud be loaded
std::vector<std::string> getActiveMods(); std::vector<std::string> getActiveMods();
CModInfo & getModData(TModID modId);
/// load content from all available mods /// load content from all available mods
void loadGameContent(); void loadGameContent();

View File

@ -828,8 +828,6 @@ void CGHeroInstance::initHero()
commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
commander->giveStackExp (exp); //after our exp is set commander->giveStackExp (exp); //after our exp is set
} }
else
commander = nullptr;
hoverName = VLC->generaltexth->allTexts[15]; hoverName = VLC->generaltexth->allTexts[15];
boost::algorithm::replace_first(hoverName,"%s",name); boost::algorithm::replace_first(hoverName,"%s",name);

View File

@ -390,7 +390,7 @@ CTown::ClientInfo::Point JsonToPoint(const JsonNode & node)
void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source) void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source)
{ {
town.clientInfo.siegePrefix = source["imagePrefix"].String(); town.clientInfo.siegePrefix = source["imagePrefix"].String();
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + source["shooter"].String(), [&town](si32 creature) VLC->modh->identifiers.requestIdentifier("creature", source["shooter"], [&town](si32 creature)
{ {
town.clientInfo.siegeShooter = CreatureID(creature); town.clientInfo.siegeShooter = CreatureID(creature);
}); });
@ -469,7 +469,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
else else
town.primaryRes = resIter - boost::begin(GameConstants::RESOURCE_NAMES); town.primaryRes = resIter - boost::begin(GameConstants::RESOURCE_NAMES);
VLC->modh->identifiers.requestIdentifier(std::string("creature." + source["warMachine"].String()), VLC->modh->identifiers.requestIdentifier("creature", source["warMachine"],
[&town](si32 creature) [&town](si32 creature)
{ {
town.warMachine = CArtHandler::creatureToMachineID(CreatureID(creature)); town.warMachine = CArtHandler::creatureToMachineID(CreatureID(creature));
@ -498,7 +498,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
for (size_t j=0; j<level.size(); j++) for (size_t j=0; j<level.size(); j++)
{ {
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + level[j].String(), [=, &town](si32 creature) VLC->modh->identifiers.requestIdentifier("creature", level[j], [=, &town](si32 creature)
{ {
town.creatures[i][j] = CreatureID(creature); town.creatures[i][j] = CreatureID(creature);
}); });
@ -510,7 +510,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
{ {
int chance = node.second.Float(); int chance = node.second.Float();
VLC->modh->identifiers.requestIdentifier("heroClass." + node.first, [=, &town](si32 classID) VLC->modh->identifiers.requestIdentifier(node.second.meta, "heroClass",node.first, [=, &town](si32 classID)
{ {
VLC->heroh->classes.heroClasses[classID]->selectionProbability[town.faction->index] = chance; VLC->heroh->classes.heroClasses[classID]->selectionProbability[town.faction->index] = chance;
}); });
@ -520,7 +520,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
{ {
int chance = node.second.Float(); int chance = node.second.Float();
VLC->modh->identifiers.requestIdentifier("spell." + node.first, [=, &town](si32 spellID) VLC->modh->identifiers.requestIdentifier(node.second.meta, "spell", node.first, [=, &town](si32 spellID)
{ {
SpellID(spellID).toSpell()->probabilities[town.faction->index] = chance; SpellID(spellID).toSpell()->probabilities[town.faction->index] = chance;
}); });
@ -568,7 +568,7 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source)
faction->name = source["name"].String(); faction->name = source["name"].String();
VLC->modh->identifiers.requestIdentifier ("creature." + source["commander"].String(), VLC->modh->identifiers.requestIdentifier ("creature", source["commander"],
[=](si32 commanderID) [=](si32 commanderID)
{ {
faction->commander = CreatureID(commanderID); faction->commander = CreatureID(commanderID);

View File

@ -50,6 +50,7 @@ JsonNode::JsonNode(ResourceID && fileURI):
JsonNode::JsonNode(const JsonNode &copy): JsonNode::JsonNode(const JsonNode &copy):
type(DATA_NULL) type(DATA_NULL)
{ {
meta = copy.meta;
setType(copy.getType()); setType(copy.getType());
switch(type) switch(type)
{ {
@ -70,6 +71,7 @@ JsonNode::~JsonNode()
void JsonNode::swap(JsonNode &b) void JsonNode::swap(JsonNode &b)
{ {
using std::swap; using std::swap;
swap(meta, b.meta);
swap(data, b.data); swap(data, b.data);
swap(type, b.type); swap(type, b.type);
} }
@ -107,6 +109,31 @@ JsonNode::JsonType JsonNode::getType() const
return type; return type;
} }
void JsonNode::setMeta(std::string metadata, bool recursive)
{
meta = metadata;
if (recursive)
{
switch (type)
{
break; case DATA_VECTOR:
{
BOOST_FOREACH(auto & node, Vector())
{
node.setMeta(metadata);
}
}
break; case DATA_STRUCT:
{
BOOST_FOREACH(auto & node, Struct())
{
node.second.setMeta(metadata);
}
}
}
}
}
void JsonNode::setType(JsonType Type) void JsonNode::setType(JsonType Type)
{ {
if (type == Type) if (type == Type)
@ -1183,17 +1210,16 @@ const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val,
void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string name) void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string name)
{ {
const JsonNode *value; const JsonNode &value = node[name];
value = &node[name]; if (!value.isNull())
if (!value->isNull())
{ {
switch (value->getType()) switch (value.getType())
{ {
case JsonNode::DATA_FLOAT: case JsonNode::DATA_FLOAT:
var = value->Float(); var = value.Float();
break; break;
case JsonNode::DATA_STRING: case JsonNode::DATA_STRING:
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier) VLC->modh->identifiers.requestIdentifier(value, [&](si32 identifier)
{ {
var = identifier; var = identifier;
}); });
@ -1212,7 +1238,7 @@ void JsonUtils::resolveIdentifier (const JsonNode &node, si32 &var)
var = node.Float(); var = node.Float();
break; break;
case JsonNode::DATA_STRING: case JsonNode::DATA_STRING:
VLC->modh->identifiers.requestIdentifier (node.String(), [&](si32 identifier) VLC->modh->identifiers.requestIdentifier (node, [&](si32 identifier)
{ {
var = identifier; var = identifier;
}); });
@ -1301,7 +1327,7 @@ Bonus * JsonUtils::parseBonus (const JsonNode &ability)
{ {
shared_ptr<CCreatureTypeLimiter> l2 = make_shared<CCreatureTypeLimiter>(); //TODO: How the hell resolve pointer to creature? shared_ptr<CCreatureTypeLimiter> l2 = make_shared<CCreatureTypeLimiter>(); //TODO: How the hell resolve pointer to creature?
const JsonVector vec = limiter["parameters"].Vector(); const JsonVector vec = limiter["parameters"].Vector();
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + vec[0].String(), [=](si32 creature) VLC->modh->identifiers.requestIdentifier("creature", vec[0], [=](si32 creature)
{ {
l2->setCreature (CreatureID(creature)); l2->setCreature (CreatureID(creature));
}); });
@ -1522,11 +1548,19 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source)
switch (source.getType()) switch (source.getType())
{ {
break; case JsonNode::DATA_NULL: dest.clear(); case JsonNode::DATA_NULL:
break; case JsonNode::DATA_BOOL: std::swap(dest.Bool(), source.Bool()); {
break; case JsonNode::DATA_FLOAT: std::swap(dest.Float(), source.Float()); dest.clear();
break; case JsonNode::DATA_STRING: std::swap(dest.String(), source.String()); break;
break; case JsonNode::DATA_VECTOR: }
case JsonNode::DATA_BOOL:
case JsonNode::DATA_FLOAT:
case JsonNode::DATA_STRING:
{
std::swap(dest, source);
break;
}
case JsonNode::DATA_VECTOR:
{ {
size_t total = std::min(source.Vector().size(), dest.Vector().size()); size_t total = std::min(source.Vector().size(), dest.Vector().size());
@ -1541,8 +1575,9 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source)
std::move(source.Vector().begin(), source.Vector().end(), std::move(source.Vector().begin(), source.Vector().end(),
std::back_inserter(dest.Vector())); std::back_inserter(dest.Vector()));
} }
break;
} }
break; case JsonNode::DATA_STRUCT: case JsonNode::DATA_STRUCT:
{ {
//recursively merge all entries from struct //recursively merge all entries from struct
BOOST_FOREACH(auto & node, source.Struct()) BOOST_FOREACH(auto & node, source.Struct())

View File

@ -46,6 +46,9 @@ private:
JsonData data; JsonData data;
public: public:
/// free to use metadata field
std::string meta;
//Create empty node //Create empty node
JsonNode(JsonType Type = DATA_NULL); JsonNode(JsonType Type = DATA_NULL);
//Create tree from Json-formatted input //Create tree from Json-formatted input
@ -63,6 +66,8 @@ public:
bool operator == (const JsonNode &other) const; bool operator == (const JsonNode &other) const;
bool operator != (const JsonNode &other) const; bool operator != (const JsonNode &other) const;
void setMeta(std::string metadata, bool recursive = true);
/// Convert node to another type. Converting to NULL will clear all data /// Convert node to another type. Converting to NULL will clear all data
void setType(JsonType Type); void setType(JsonType Type);
JsonType getType() const; JsonType getType() const;
@ -101,6 +106,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & meta;
// simple saving - save json in its string interpretation // simple saving - save json in its string interpretation
if (h.saving) if (h.saving)
{ {

View File

@ -30,6 +30,7 @@ do
--dest) dest_dir=$2 ; shift 2 ;; --dest) dest_dir=$2 ; shift 2 ;;
--wog) wog_archive=$2 ; shift 2 ;; --wog) wog_archive=$2 ; shift 2 ;;
--vcmi) vcmi_archive=$2 ; shift 2 ;; --vcmi) vcmi_archive=$2 ; shift 2 ;;
--convertMP3) useffmpeg=true; shift 1 ;;
--download) download=true ; shift 1 ;; --download) download=true ; shift 1 ;;
--validate) validate=true ; shift 1 ;; --validate) validate=true ; shift 1 ;;
*) print_help=true ; shift 1 ;; *) print_help=true ; shift 1 ;;
@ -57,12 +58,15 @@ then
echo " --vcmi ARCHIVE " "Path to manually downloaded VCMI data package" echo " --vcmi ARCHIVE " "Path to manually downloaded VCMI data package"
echo " " "Requires unzip" echo " " "Requires unzip"
echo echo
echo " --download " "If specified vcmibuilder will download packages using wget" echo " --convertMP3 " "Convert all mp3 files into ogg/vorbis"
echo " " "Requires ffmpeg"
echo
echo " --download " "Automatically download requied packages using wget"
echo " " "Requires wget and Internet connection" echo " " "Requires wget and Internet connection"
echo echo
echo " --dest DIRECTORY " "Path where resulting data will be placed. Default is ~/.vcmi" echo " --dest DIRECTORY " "Path where resulting data will be placed. Default is ~/.vcmi"
echo echo
echo " --validate " "If specified vcmibuilder will run basic validness checks" echo " --validate " "Run basic validness checks"
exit 0 exit 0
fi fi
@ -110,6 +114,11 @@ then
fi fi
fi fi
if [[ -n "$useffmpeg" ]]
then
test_utility "ffmpeg" "-version"
fi
if [[ -n "$cd1_dir" ]] if [[ -n "$cd1_dir" ]]
then then
test_utility "unshield" "-V" test_utility "unshield" "-V"
@ -251,6 +260,25 @@ then
unzip -qo "$vcmi_archive" -d "$dest_dir" || fail "Error: failed to extract VCMI archive!" unzip -qo "$vcmi_archive" -d "$dest_dir" || fail "Error: failed to extract VCMI archive!"
fi fi
if [[ -n "$useffmpeg" ]]
then
# now when all music files (including WoG theme) were installed convert them to ogg
echo "Converting mp3 files..."
OIFS="$IFS"
IFS=$'\n'
for file in `find "$dest_dir" -type f -name "*.mp3"`
do
echo "Converting $file"
ogg=${file%.*}
ffmpeg -y -i "$file" -acodec libvorbis "$ogg".ogg 2>/dev/null || fail "Error: failed to convert $file to ogg/vorbis using ffmpeg" "rm -f "$ogg".ogg"
rm -f $file
done
IFS="$OIFS"
fi
if [[ -n "$validate" ]] if [[ -n "$validate" ]]
then then
test -f "$dest_dir"/Data/H3bitmap.lod || fail "Error: Heroes 3 data files are missing!" test -f "$dest_dir"/Data/H3bitmap.lod || fail "Error: Heroes 3 data files are missing!"