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:
parent
be6aff5173
commit
8273f323b1
38
Mods/WoG/config/wog/factions.json
Normal file
38
Mods/WoG/config/wog/factions.json
Normal 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"
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
{
|
||||
"filesystem":
|
||||
{
|
||||
"CONFIG/" :
|
||||
[
|
||||
{ "type" : "dir", "path" : "/Config"}
|
||||
],
|
||||
"DATA/" :
|
||||
[
|
||||
{"type" : "lod", "path" : "/Data/hmm35wog.pac"},
|
||||
@ -34,5 +38,15 @@
|
||||
},
|
||||
|
||||
"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"
|
||||
]
|
||||
}
|
||||
|
@ -4417,6 +4417,12 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
|
||||
bonusValue = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
baseType = CComponent::artifact;
|
||||
type = art->artType->iconIndex;
|
||||
bonusValue = 0;
|
||||
}
|
||||
|
||||
if (locked) // Locks should appear as empty.
|
||||
hoverText = CGI->generaltexth->allTexts[507];
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 0,
|
||||
"nativeTerrain": "grass",
|
||||
"alignment" : "good",
|
||||
"commander" : "paladin1",
|
||||
"commander" : "zealot",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASCAS",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 8,
|
||||
"nativeTerrain": "grass",
|
||||
"alignment" : "neutral",
|
||||
"commander" : "astralSpirit1",
|
||||
"commander" : "iceElemental",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASELE",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 5,
|
||||
"nativeTerrain": "subterra",
|
||||
"alignment" : "evil",
|
||||
"commander" : "brute1",
|
||||
"commander" : "medusaQueen",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASDUN",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 7,
|
||||
"nativeTerrain": "swamp",
|
||||
"alignment" : "neutral",
|
||||
"commander" : "shaman1",
|
||||
"commander" : "lizardWarrior",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASFOR",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 3,
|
||||
"nativeTerrain": "lava",
|
||||
"alignment" : "evil",
|
||||
"commander" : "succubus1",
|
||||
"commander" : "magog",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASINF",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 4,
|
||||
"nativeTerrain": "dirt",
|
||||
"alignment" : "evil",
|
||||
"commander" : "soulEater1",
|
||||
"commander" : "powerLich",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASNEC",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 1,
|
||||
"nativeTerrain": "grass",
|
||||
"alignment" : "good",
|
||||
"commander" : "hierophant1",
|
||||
"commander" : "grandElf",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASRAM",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 6,
|
||||
"nativeTerrain": "rough",
|
||||
"alignment" : "neutral",
|
||||
"commander" : "ogreLeader1",
|
||||
"commander" : "orcChieftain",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASSTR",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"index" : 2,
|
||||
"nativeTerrain" : "snow",
|
||||
"alignment" : "good",
|
||||
"commander" : "templeGuardian1",
|
||||
"commander" : "archMage",
|
||||
"creatureBackground" :
|
||||
{
|
||||
"120px" : "TPCASTOW",
|
||||
|
@ -28,8 +28,7 @@
|
||||
"config/creatures/conflux.json",
|
||||
|
||||
"config/creatures/neutral.json",
|
||||
"config/creatures/special.json",
|
||||
"config/creatures/wog.json"
|
||||
"config/creatures/special.json"
|
||||
],
|
||||
|
||||
"heroes" :
|
||||
|
@ -366,7 +366,7 @@ void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
|
||||
art->constituents.reset(new std::vector<CArtifact *>());
|
||||
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
|
||||
// so it is safe to access any of them
|
||||
|
@ -179,7 +179,10 @@ CCreatureHandler::CCreatureHandler()
|
||||
|
||||
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())
|
||||
{
|
||||
@ -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;
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
|
@ -105,14 +105,14 @@ CHeroClass *CHeroClassHandler::loadFromJson(const JsonNode & node)
|
||||
{
|
||||
int value = tavern.second.Float();
|
||||
|
||||
VLC->modh->identifiers.requestIdentifier("faction." + tavern.first,
|
||||
VLC->modh->identifiers.requestIdentifier(tavern.second.meta, "faction", tavern.first,
|
||||
[=](si32 factionID)
|
||||
{
|
||||
heroClass->selectionProbability[factionID] = value;
|
||||
});
|
||||
}
|
||||
|
||||
VLC->modh->identifiers.requestIdentifier("faction." + node["faction"].String(),
|
||||
VLC->modh->identifiers.requestIdentifier("faction", node["faction"],
|
||||
[=](si32 factionID)
|
||||
{
|
||||
heroClass->faction = factionID;
|
||||
@ -237,7 +237,7 @@ CHero * CHeroHandler::loadFromJson(const JsonNode & node)
|
||||
loadHeroSkills(hero, node);
|
||||
loadHeroSpecialty(hero, node);
|
||||
|
||||
VLC->modh->identifiers.requestIdentifier("heroClass." + node["class"].String(),
|
||||
VLC->modh->identifiers.requestIdentifier("heroClass", node["class"],
|
||||
[=](si32 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);
|
||||
|
||||
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);
|
||||
});
|
||||
@ -278,7 +278,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
|
||||
size_t currentIndex = hero->secSkillsInit.size();
|
||||
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);
|
||||
});
|
||||
@ -300,7 +300,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
|
||||
}
|
||||
else
|
||||
{
|
||||
VLC->modh->identifiers.requestIdentifier("spell." + spell.String(),
|
||||
VLC->modh->identifiers.requestIdentifier("spell", spell,
|
||||
[=](si32 spellID)
|
||||
{
|
||||
hero->spells.insert(SpellID(spellID));
|
||||
|
@ -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
|
||||
/* auto iter = registeredObjects.find(name);
|
||||
|
||||
if (iter != registeredObjects.end())
|
||||
callback(iter->second); //already registered - trigger callback immediately
|
||||
if (splitPos == std::string::npos)
|
||||
{
|
||||
ret.first.clear();
|
||||
ret.second = input;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(boost::algorithm::starts_with(name, "primSkill."))
|
||||
logGlobal->warnStream() << "incorrect primSkill name requested";
|
||||
ret.first = input.substr(0, splitPos);
|
||||
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)
|
||||
{
|
||||
//TODO: use scope
|
||||
ObjectData data;
|
||||
data.scope = scope;
|
||||
data.id = identifier;
|
||||
|
||||
std::string fullID = type + '.' + name;
|
||||
checkIdentifier(fullID);
|
||||
|
||||
// do not allow to register same object twice
|
||||
assert(registeredObjects.find(fullID) == registeredObjects.end());
|
||||
|
||||
registeredObjects[fullID] = identifier;
|
||||
|
||||
// old version with immediate callback posibility. Can't be used for some cases
|
||||
/*auto iter = missingObjects.find(fullID);
|
||||
if (iter != missingObjects.end())
|
||||
{
|
||||
//call all awaiting callbacks
|
||||
BOOST_FOREACH(auto function, iter->second)
|
||||
{
|
||||
function(identifier);
|
||||
registeredObjects.insert(std::make_pair(fullID, data));
|
||||
}
|
||||
missingObjects.erase(iter);
|
||||
}*/
|
||||
|
||||
bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
|
||||
{
|
||||
std::set<std::string> allowedScopes;
|
||||
|
||||
if (request.remoteScope.empty())
|
||||
{
|
||||
// normally ID's from all required mods, own mod and virtual "core" mod are allowed
|
||||
if (request.localScope != "core")
|
||||
allowedScopes = VLC->modh->getModData(request.localScope).dependencies;
|
||||
|
||||
allowedScopes.insert(request.localScope);
|
||||
allowedScopes.insert("core");
|
||||
}
|
||||
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()
|
||||
{
|
||||
for (auto it = missingObjects.begin(); it!= missingObjects.end();)
|
||||
bool errorsFound = false;
|
||||
|
||||
BOOST_FOREACH(const ObjectCallback & request, scheduledRequests)
|
||||
{
|
||||
auto object = registeredObjects.find(it->first);
|
||||
if (object != registeredObjects.end())
|
||||
{
|
||||
BOOST_FOREACH(auto function, it->second)
|
||||
{
|
||||
function(object->second);
|
||||
}
|
||||
it = missingObjects.erase(it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
errorsFound |= !resolveIdentifier(request);
|
||||
}
|
||||
|
||||
// print list of missing objects and crash
|
||||
// in future should try to do some cleanup (like returning all id's as 0)
|
||||
if (!missingObjects.empty())
|
||||
if (errorsFound)
|
||||
{
|
||||
BOOST_FOREACH(auto object, missingObjects)
|
||||
{
|
||||
logGlobal->errorStream() << "Error: object " << object.first << " was not found!";
|
||||
}
|
||||
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";
|
||||
}
|
||||
assert(missingObjects.empty());
|
||||
assert(errorsFound == false);
|
||||
}
|
||||
|
||||
CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, size_t size, std::string objectName):
|
||||
@ -127,11 +187,16 @@ CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler,
|
||||
objectName(objectName),
|
||||
originalData(handler->loadLegacyData(size))
|
||||
{
|
||||
BOOST_FOREACH(auto & node, originalData)
|
||||
{
|
||||
node.setMeta("core");
|
||||
}
|
||||
}
|
||||
|
||||
void CContentHandler::ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList)
|
||||
{
|
||||
JsonNode data = JsonUtils::assembleFromFiles(fileList);
|
||||
data.setMeta(modName);
|
||||
|
||||
ModInfo & modInfo = modData[modName];
|
||||
|
||||
@ -425,6 +490,13 @@ std::vector<std::string> CModHandler::getActiveMods()
|
||||
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>
|
||||
void CModHandler::handleData(Handler handler, const JsonNode & source, std::string listName, std::string schemaName)
|
||||
{
|
||||
@ -447,7 +519,7 @@ void CModHandler::loadGameContent()
|
||||
CContentHandler content;
|
||||
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
|
||||
content.preloadModData("core", JsonNode(ResourceID("config/gameConfig.json")));
|
||||
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() << "\tDone loading data";
|
||||
|
||||
VLC->creh->loadCrExpBon();
|
||||
VLC->creh->buildBonusTreeForTiers(); //do that after all new creatures are loaded
|
||||
identifiers.finalize();
|
||||
|
||||
logGlobal->infoStream() << "\tResolving identifiers: " << timer.getDiff() << " ms";
|
||||
logGlobal->infoStream() << "\tAll game content loaded in " << totalTime.getDiff() << " ms";
|
||||
}
|
||||
|
||||
|
@ -25,15 +25,38 @@ class IHandlerBase;
|
||||
/// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
|
||||
class CIdentifierStorage
|
||||
{
|
||||
std::map<std::string, si32 > registeredObjects;
|
||||
std::map<std::string, std::vector<boost::function<void(si32)> > > missingObjects;
|
||||
struct ObjectCallback // entry created on ID request
|
||||
{
|
||||
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)
|
||||
void checkIdentifier(std::string & ID);
|
||||
|
||||
void requestIdentifier(ObjectCallback callback);
|
||||
bool resolveIdentifier(const ObjectCallback & callback);
|
||||
public:
|
||||
/// request identifier for specific object name. If ID is not yet resolved callback will be queued
|
||||
/// 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
|
||||
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
|
||||
std::vector<std::string> getActiveMods();
|
||||
|
||||
CModInfo & getModData(TModID modId);
|
||||
|
||||
/// load content from all available mods
|
||||
void loadGameContent();
|
||||
|
||||
|
@ -828,8 +828,6 @@ void CGHeroInstance::initHero()
|
||||
commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
|
||||
commander->giveStackExp (exp); //after our exp is set
|
||||
}
|
||||
else
|
||||
commander = nullptr;
|
||||
|
||||
hoverName = VLC->generaltexth->allTexts[15];
|
||||
boost::algorithm::replace_first(hoverName,"%s",name);
|
||||
|
@ -390,7 +390,7 @@ CTown::ClientInfo::Point JsonToPoint(const JsonNode & node)
|
||||
void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source)
|
||||
{
|
||||
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);
|
||||
});
|
||||
@ -469,7 +469,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
|
||||
else
|
||||
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.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++)
|
||||
{
|
||||
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);
|
||||
});
|
||||
@ -510,7 +510,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
|
||||
{
|
||||
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;
|
||||
});
|
||||
@ -520,7 +520,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
|
||||
{
|
||||
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;
|
||||
});
|
||||
@ -568,7 +568,7 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source)
|
||||
|
||||
faction->name = source["name"].String();
|
||||
|
||||
VLC->modh->identifiers.requestIdentifier ("creature." + source["commander"].String(),
|
||||
VLC->modh->identifiers.requestIdentifier ("creature", source["commander"],
|
||||
[=](si32 commanderID)
|
||||
{
|
||||
faction->commander = CreatureID(commanderID);
|
||||
|
@ -50,6 +50,7 @@ JsonNode::JsonNode(ResourceID && fileURI):
|
||||
JsonNode::JsonNode(const JsonNode ©):
|
||||
type(DATA_NULL)
|
||||
{
|
||||
meta = copy.meta;
|
||||
setType(copy.getType());
|
||||
switch(type)
|
||||
{
|
||||
@ -70,6 +71,7 @@ JsonNode::~JsonNode()
|
||||
void JsonNode::swap(JsonNode &b)
|
||||
{
|
||||
using std::swap;
|
||||
swap(meta, b.meta);
|
||||
swap(data, b.data);
|
||||
swap(type, b.type);
|
||||
}
|
||||
@ -107,6 +109,31 @@ JsonNode::JsonType JsonNode::getType() const
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
const JsonNode *value;
|
||||
value = &node[name];
|
||||
if (!value->isNull())
|
||||
const JsonNode &value = node[name];
|
||||
if (!value.isNull())
|
||||
{
|
||||
switch (value->getType())
|
||||
switch (value.getType())
|
||||
{
|
||||
case JsonNode::DATA_FLOAT:
|
||||
var = value->Float();
|
||||
var = value.Float();
|
||||
break;
|
||||
case JsonNode::DATA_STRING:
|
||||
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier)
|
||||
VLC->modh->identifiers.requestIdentifier(value, [&](si32 identifier)
|
||||
{
|
||||
var = identifier;
|
||||
});
|
||||
@ -1212,7 +1238,7 @@ void JsonUtils::resolveIdentifier (const JsonNode &node, si32 &var)
|
||||
var = node.Float();
|
||||
break;
|
||||
case JsonNode::DATA_STRING:
|
||||
VLC->modh->identifiers.requestIdentifier (node.String(), [&](si32 identifier)
|
||||
VLC->modh->identifiers.requestIdentifier (node, [&](si32 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?
|
||||
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));
|
||||
});
|
||||
@ -1522,11 +1548,19 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source)
|
||||
|
||||
switch (source.getType())
|
||||
{
|
||||
break; case JsonNode::DATA_NULL: dest.clear();
|
||||
break; case JsonNode::DATA_BOOL: std::swap(dest.Bool(), source.Bool());
|
||||
break; case JsonNode::DATA_FLOAT: std::swap(dest.Float(), source.Float());
|
||||
break; case JsonNode::DATA_STRING: std::swap(dest.String(), source.String());
|
||||
break; case JsonNode::DATA_VECTOR:
|
||||
case JsonNode::DATA_NULL:
|
||||
{
|
||||
dest.clear();
|
||||
break;
|
||||
}
|
||||
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());
|
||||
|
||||
@ -1541,8 +1575,9 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source)
|
||||
std::move(source.Vector().begin(), source.Vector().end(),
|
||||
std::back_inserter(dest.Vector()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break; case JsonNode::DATA_STRUCT:
|
||||
case JsonNode::DATA_STRUCT:
|
||||
{
|
||||
//recursively merge all entries from struct
|
||||
BOOST_FOREACH(auto & node, source.Struct())
|
||||
|
@ -46,6 +46,9 @@ private:
|
||||
JsonData data;
|
||||
|
||||
public:
|
||||
/// free to use metadata field
|
||||
std::string meta;
|
||||
|
||||
//Create empty node
|
||||
JsonNode(JsonType Type = DATA_NULL);
|
||||
//Create tree from Json-formatted input
|
||||
@ -63,6 +66,8 @@ public:
|
||||
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
|
||||
void setType(JsonType Type);
|
||||
JsonType getType() const;
|
||||
@ -101,6 +106,7 @@ public:
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & meta;
|
||||
// simple saving - save json in its string interpretation
|
||||
if (h.saving)
|
||||
{
|
||||
|
32
vcmibuilder
32
vcmibuilder
@ -30,6 +30,7 @@ do
|
||||
--dest) dest_dir=$2 ; shift 2 ;;
|
||||
--wog) wog_archive=$2 ; shift 2 ;;
|
||||
--vcmi) vcmi_archive=$2 ; shift 2 ;;
|
||||
--convertMP3) useffmpeg=true; shift 1 ;;
|
||||
--download) download=true ; shift 1 ;;
|
||||
--validate) validate=true ; shift 1 ;;
|
||||
*) print_help=true ; shift 1 ;;
|
||||
@ -57,12 +58,15 @@ then
|
||||
echo " --vcmi ARCHIVE " "Path to manually downloaded VCMI data package"
|
||||
echo " " "Requires unzip"
|
||||
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
|
||||
echo " --dest DIRECTORY " "Path where resulting data will be placed. Default is ~/.vcmi"
|
||||
echo
|
||||
echo " --validate " "If specified vcmibuilder will run basic validness checks"
|
||||
echo " --validate " "Run basic validness checks"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@ -110,6 +114,11 @@ then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$useffmpeg" ]]
|
||||
then
|
||||
test_utility "ffmpeg" "-version"
|
||||
fi
|
||||
|
||||
if [[ -n "$cd1_dir" ]]
|
||||
then
|
||||
test_utility "unshield" "-V"
|
||||
@ -251,6 +260,25 @@ then
|
||||
unzip -qo "$vcmi_archive" -d "$dest_dir" || fail "Error: failed to extract VCMI archive!"
|
||||
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" ]]
|
||||
then
|
||||
test -f "$dest_dir"/Data/H3bitmap.lod || fail "Error: Heroes 3 data files are missing!"
|
||||
|
Loading…
Reference in New Issue
Block a user