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":
|
"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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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" :
|
||||||
|
@ -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
|
||||||
|
@ -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));
|
||||||
});
|
});
|
||||||
|
@ -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));
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
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()
|
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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -50,6 +50,7 @@ JsonNode::JsonNode(ResourceID && fileURI):
|
|||||||
JsonNode::JsonNode(const JsonNode ©):
|
JsonNode::JsonNode(const JsonNode ©):
|
||||||
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())
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
32
vcmibuilder
32
vcmibuilder
@ -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!"
|
||||||
|
Loading…
Reference in New Issue
Block a user