mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
- basic support for dependencies\conflicts for mods
- adventure map objects transparency should be more similar to h3
This commit is contained in:
parent
d31277c3cc
commit
7e7d12095b
@ -34,6 +34,5 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"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"
|
||||||
"priority" : 5
|
|
||||||
}
|
}
|
||||||
|
@ -17,5 +17,9 @@
|
|||||||
|
|
||||||
"name" : "VCMI essential files",
|
"name" : "VCMI essential files",
|
||||||
"description" : "Essential files required for VCMI to run correctly",
|
"description" : "Essential files required for VCMI to run correctly",
|
||||||
"priority" : 10
|
|
||||||
|
"requires" :
|
||||||
|
[
|
||||||
|
"wog"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -5911,20 +5911,20 @@ void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation &artLoc)
|
|||||||
|
|
||||||
void CWindowWithArtifacts::artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)
|
void CWindowWithArtifacts::artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)
|
||||||
{
|
{
|
||||||
CArtifactsOfHero *destaoh = NULL;
|
CArtifactsOfHero *destaoh = NULL;
|
||||||
BOOST_FOREACH(CArtifactsOfHero *aoh, artSets)
|
BOOST_FOREACH(CArtifactsOfHero *aoh, artSets)
|
||||||
{
|
{
|
||||||
aoh->artifactMoved(artLoc, destLoc);
|
aoh->artifactMoved(artLoc, destLoc);
|
||||||
aoh->redraw();
|
aoh->redraw();
|
||||||
if(destLoc.isHolder(aoh->getHero()))
|
if(destLoc.isHolder(aoh->getHero()))
|
||||||
destaoh = aoh;
|
destaoh = aoh;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make sure the status bar is updated so it does not display old text
|
//Make sure the status bar is updated so it does not display old text
|
||||||
if(destaoh != NULL)
|
if(destaoh != NULL && destaoh->getArtPlace(destLoc.slot) != NULL)
|
||||||
{
|
{
|
||||||
destaoh->getArtPlace(destLoc.slot)->hover(true);
|
destaoh->getArtPlace(destLoc.slot)->hover(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation &artLoc)
|
void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation &artLoc)
|
||||||
|
@ -194,13 +194,20 @@ Uint32 CSDL_Ext::SDL_GetPixel(SDL_Surface *surface, const int & x, const int & y
|
|||||||
|
|
||||||
void CSDL_Ext::alphaTransform(SDL_Surface *src)
|
void CSDL_Ext::alphaTransform(SDL_Surface *src)
|
||||||
{
|
{
|
||||||
//NOTE: colors #7 & #8 used in some of WoG objects. Don't know how they're handled by H3
|
|
||||||
assert(src->format->BitsPerPixel == 8);
|
assert(src->format->BitsPerPixel == 8);
|
||||||
SDL_Color colors[] = {{0,0,0,0}, {0,0,0,32}, {0,0,0,64}, {0,0,0,128}, {0,0,0,128},
|
SDL_Color colors[] =
|
||||||
{255,255,255,0}, {255,255,255,0}, {255,255,255,0}, {0,0,0,192}, {0,0,0,192}};
|
{
|
||||||
|
{ 0, 0, 0, 0}, { 0, 0, 0, 32}, { 0, 0, 0, 64},
|
||||||
|
{ 0, 0, 0, 128}, { 0, 0, 0, 128}
|
||||||
|
};
|
||||||
|
|
||||||
SDL_SetColors(src, colors, 0, ARRAY_COUNT(colors));
|
|
||||||
SDL_SetColorKey(src, SDL_SRCCOLORKEY, SDL_MapRGBA(src->format, 0, 0, 0, 255));
|
for (size_t i=0; i< ARRAY_COUNT(colors); i++ )
|
||||||
|
{
|
||||||
|
SDL_Color & palColor = src->format->palette->colors[i];
|
||||||
|
palColor = colors[i];
|
||||||
|
}
|
||||||
|
SDL_SetColorKey(src, SDL_SRCCOLORKEY, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepareOutRect(SDL_Rect *src, SDL_Rect *dst, const SDL_Rect & clip_rect)
|
static void prepareOutRect(SDL_Rect *src, SDL_Rect *dst, const SDL_Rect & clip_rect)
|
||||||
|
@ -115,37 +115,155 @@ void CModHandler::loadConfigFromFile (std::string name)
|
|||||||
modules.MITHRIL = gameModules["MITHRIL"].Bool();
|
modules.MITHRIL = gameModules["MITHRIL"].Bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// currentList is passed by value to get current list of depending mods
|
||||||
|
bool CModHandler::hasCircularDependency(TModID modID, std::set <TModID> currentList) const
|
||||||
|
{
|
||||||
|
const CModInfo & mod = allMods.at(modID);
|
||||||
|
|
||||||
|
// Mod already present? We found a loop
|
||||||
|
if (vstd::contains(currentList, modID))
|
||||||
|
{
|
||||||
|
tlog0 << "Error: Circular dependency detected! Printing dependency list:\n";
|
||||||
|
tlog0 << "\t" << mod.name << " -> \n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentList.insert(modID);
|
||||||
|
|
||||||
|
// recursively check every dependency of this mod
|
||||||
|
BOOST_FOREACH(const TModID & dependency, mod.dependencies)
|
||||||
|
{
|
||||||
|
if (hasCircularDependency(dependency, currentList))
|
||||||
|
{
|
||||||
|
tlog0 << "\t" << mod.name << " ->\n"; // conflict detected, print dependency list
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CModHandler::checkDependencies(const std::vector <TModID> & input) const
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(const TModID & id, input)
|
||||||
|
{
|
||||||
|
const CModInfo & mod = allMods.at(id);
|
||||||
|
|
||||||
|
BOOST_FOREACH(const TModID & dep, mod.dependencies)
|
||||||
|
{
|
||||||
|
if (!vstd::contains(input, dep))
|
||||||
|
{
|
||||||
|
tlog0 << "Error: Mod " << mod.name << " requires missing " << dep << "!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FOREACH(const TModID & conflicting, mod.conflicts)
|
||||||
|
{
|
||||||
|
if (vstd::contains(input, conflicting))
|
||||||
|
{
|
||||||
|
tlog0 << "Error: Mod " << mod.name << " conflicts with " << allMods.at(conflicting).name << "!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasCircularDependency(id))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector <TModID> CModHandler::resolveDependencies(std::vector <TModID> input) const
|
||||||
|
{
|
||||||
|
// Algorithm may not be the fastest one but VCMI does not needs any speed here
|
||||||
|
// Unless user have dozens of mods with complex dependencies this cide should be fine
|
||||||
|
|
||||||
|
std::vector <TModID> output;
|
||||||
|
output.reserve(input.size());
|
||||||
|
|
||||||
|
std::set <TModID> resolvedMods;
|
||||||
|
|
||||||
|
// Check if all mod dependencies are resolved (moved to resolvedMods)
|
||||||
|
auto isResolved = [&](const CModInfo mod) -> bool
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(const TModID & dependency, mod.dependencies)
|
||||||
|
{
|
||||||
|
if (!vstd::contains(resolvedMods, dependency))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (!input.empty())
|
||||||
|
{
|
||||||
|
for (auto it = input.begin(); it != input.end();)
|
||||||
|
{
|
||||||
|
if (isResolved(allMods.at(*it)))
|
||||||
|
{
|
||||||
|
resolvedMods.insert(*it);
|
||||||
|
output.push_back(*it);
|
||||||
|
it = input.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
void CModHandler::initialize(std::vector<std::string> availableMods)
|
void CModHandler::initialize(std::vector<std::string> availableMods)
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(std::string &name, availableMods)
|
JsonNode modConfig(ResourceID("config/modSettings.json"));
|
||||||
|
const JsonNode & modList = modConfig["activeMods"];
|
||||||
|
JsonNode resultingList;
|
||||||
|
|
||||||
|
std::vector <TModID> detectedMods;
|
||||||
|
|
||||||
|
BOOST_FOREACH(std::string name, availableMods)
|
||||||
{
|
{
|
||||||
|
boost::to_lower(name);
|
||||||
std::string modFileName = "mods/" + name + "/mod.json";
|
std::string modFileName = "mods/" + name + "/mod.json";
|
||||||
|
|
||||||
if (CResourceHandler::get()->existsResource(ResourceID(modFileName)))
|
if (CResourceHandler::get()->existsResource(ResourceID(modFileName)))
|
||||||
{
|
{
|
||||||
const JsonNode config = JsonNode(ResourceID(modFileName));
|
const JsonNode config = JsonNode(ResourceID(modFileName));
|
||||||
|
|
||||||
if (!config.isNull())
|
if (config.isNull())
|
||||||
{
|
continue;
|
||||||
allMods[name].identifier = name;
|
|
||||||
allMods[name].name = config["name"].String();
|
|
||||||
allMods[name].description = config["description"].String();
|
|
||||||
allMods[name].loadPriority = config["priority"].Float();
|
|
||||||
activeMods.push_back(name);
|
|
||||||
|
|
||||||
tlog1 << "\t\tMod ";
|
if (!modList[name].isNull() && modList[name].Bool() == false )
|
||||||
tlog2 << allMods[name].name;
|
{
|
||||||
tlog1 << " enabled\n";
|
resultingList[name].Bool() = false;
|
||||||
|
continue; // disabled mod
|
||||||
}
|
}
|
||||||
|
resultingList[name].Bool() = true;
|
||||||
|
|
||||||
|
CModInfo & mod = allMods[name];
|
||||||
|
|
||||||
|
mod.identifier = name;
|
||||||
|
mod.name = config["name"].String();
|
||||||
|
mod.description = config["description"].String();
|
||||||
|
mod.dependencies = config["depends"].convertTo<std::set<std::string> >();
|
||||||
|
mod.conflicts = config["conflicts"].convertTo<std::set<std::string> >();
|
||||||
|
detectedMods.push_back(name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
tlog1 << "\t\t Directory " << name << " does not contains VCMI mod\n";
|
tlog1 << "\t\t Directory " << name << " does not contains VCMI mod\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(activeMods.begin(), activeMods.end(), [&](std::string a, std::string b)
|
if (!checkDependencies(detectedMods))
|
||||||
{
|
{
|
||||||
return allMods[a].loadPriority < allMods[b].loadPriority;
|
tlog0 << "Critical error: failed to load mods! Exiting...\n";
|
||||||
});
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
activeMods = resolveDependencies(detectedMods);
|
||||||
|
|
||||||
|
modConfig["activeMods"] = resultingList;
|
||||||
|
CResourceHandler::get()->createResource("CONFIG/modSettings.json");
|
||||||
|
|
||||||
|
std::ofstream file(CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::trunc);
|
||||||
|
file << modConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -162,8 +280,11 @@ void handleData(Handler handler, const JsonNode & config)
|
|||||||
|
|
||||||
void CModHandler::loadActiveMods()
|
void CModHandler::loadActiveMods()
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(std::string & modName, activeMods)
|
BOOST_FOREACH(const TModID & modName, activeMods)
|
||||||
{
|
{
|
||||||
|
tlog1 << "\t\tLoading mod ";
|
||||||
|
tlog2 << allMods[modName].name << "\n";
|
||||||
|
|
||||||
std::string modFileName = "mods/" + modName + "/mod.json";
|
std::string modFileName = "mods/" + modName + "/mod.json";
|
||||||
|
|
||||||
const JsonNode config = JsonNode(ResourceID(modFileName));
|
const JsonNode config = JsonNode(ResourceID(modFileName));
|
||||||
|
@ -51,19 +51,18 @@ public:
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
||||||
/// priority in which this mod should be loaded
|
/// list of mods that should be loaded before this one
|
||||||
/// may be somewhat ignored to load required mods first or overriden by user
|
std::set <TModID> dependencies;
|
||||||
double loadPriority;
|
|
||||||
|
|
||||||
/// TODO: list of mods that should be loaded before this one
|
/// list of mods that can't be used in the same time as this one
|
||||||
std::set <TModID> requirements;
|
std::set <TModID> conflicts;
|
||||||
|
|
||||||
// mod configuration (mod.json). (no need to store it right now)
|
// mod configuration (mod.json). (no need to store it right now)
|
||||||
// std::shared_ptr<JsonNode> config; //TODO: unique_ptr can't be serialized
|
// std::shared_ptr<JsonNode> config; //TODO: unique_ptr can't be serialized
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & name & requirements;
|
h & identifier & description & name & dependencies & conflicts;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,6 +72,18 @@ class DLL_LINKAGE CModHandler
|
|||||||
std::vector <TModID> activeMods;//active mods, in order in which they were loaded
|
std::vector <TModID> activeMods;//active mods, in order in which they were loaded
|
||||||
|
|
||||||
void loadConfigFromFile (std::string name);
|
void loadConfigFromFile (std::string name);
|
||||||
|
|
||||||
|
bool hasCircularDependency(TModID mod, std::set <TModID> currentList = std::set <TModID>()) const;
|
||||||
|
|
||||||
|
//returns false if mod list is incorrec and prints error to console. Possible errors are:
|
||||||
|
// - missing dependency mod
|
||||||
|
// - conflicting mod in load order
|
||||||
|
// - circular dependencies
|
||||||
|
bool checkDependencies(const std::vector <TModID> & input) const;
|
||||||
|
|
||||||
|
// returns load order in which all dependencies are resolved, e.g. loaded after required mods
|
||||||
|
// function assumes that input list is valid (checkDependencies returned true)
|
||||||
|
std::vector <TModID> resolveDependencies(std::vector<TModID> input) const;
|
||||||
public:
|
public:
|
||||||
CIdentifierStorage identifiers;
|
CIdentifierStorage identifiers;
|
||||||
|
|
||||||
|
@ -6894,39 +6894,51 @@ void CArmedInstance::updateMoraleBonusFromArmy()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//number of alignments and presence of undead
|
//number of alignments and presence of undead
|
||||||
bool canMix = hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX);
|
std::set<TFaction> factions;
|
||||||
std::set<si8> factions;
|
bool hasUndead = false;
|
||||||
for(TSlots::const_iterator i=Slots().begin(); i!=Slots().end(); i++)
|
|
||||||
|
BOOST_FOREACH(auto slot, Slots())
|
||||||
{
|
{
|
||||||
// Take Angelic Alliance troop-mixing freedom of non-evil, non-Conflux units into account.
|
const CStackInstance * inst = slot.second;
|
||||||
const si8 faction = i->second->type->faction;
|
const CCreature * creature = VLC->creh->creatures[inst->getCreatureID()];
|
||||||
if (canMix
|
|
||||||
&& ((faction >= 0 && faction <= 2) || faction == 6 || faction == 7))
|
factions.insert(creature->faction);
|
||||||
{
|
// Check for undead flag instead of faction (undead mummies are neutral)
|
||||||
factions.insert(0); // Insert a single faction of the affected group, Castle will do.
|
hasUndead |= inst->hasBonusOfType(Bonus::UNDEAD);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
factions.insert(faction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(factions.size() == 1)
|
size_t factionsInArmy = factions.size();
|
||||||
|
|
||||||
|
// Take Angelic Alliance troop-mixing freedom of non-evil units into account.
|
||||||
|
if (hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX))
|
||||||
|
{
|
||||||
|
size_t mixableFactions = 0;
|
||||||
|
|
||||||
|
BOOST_FOREACH(TFaction f, factions)
|
||||||
|
{
|
||||||
|
if (VLC->townh->factions[f].alignment != EAlignment::EVIL)
|
||||||
|
mixableFactions++;
|
||||||
|
}
|
||||||
|
if (mixableFactions > 0)
|
||||||
|
factionsInArmy -= mixableFactions - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(factionsInArmy == 1)
|
||||||
{
|
{
|
||||||
b->val = +1;
|
b->val = +1;
|
||||||
b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
|
b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
b->val = 2-factions.size();
|
b->val = 2 - factionsInArmy;
|
||||||
b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factions.size() % b->val); //Troops of %d alignments %d
|
b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
|
||||||
}
|
}
|
||||||
boost::algorithm::trim(b->description);
|
boost::algorithm::trim(b->description);
|
||||||
|
|
||||||
//-1 modifier for any Necropolis unit in army
|
//-1 modifier for any Undead unit in army
|
||||||
const ui8 UNDEAD_MODIFIER_ID = -2;
|
const ui8 UNDEAD_MODIFIER_ID = -2;
|
||||||
Bonus *undeadModifier = getBonusList().getFirst(Selector::source(Bonus::ARMY, UNDEAD_MODIFIER_ID));
|
Bonus *undeadModifier = getBonusList().getFirst(Selector::source(Bonus::ARMY, UNDEAD_MODIFIER_ID));
|
||||||
if(vstd::contains(factions, ETownType::NECROPOLIS))
|
if(hasUndead)
|
||||||
{
|
{
|
||||||
if(!undeadModifier)
|
if(!undeadModifier)
|
||||||
addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]));
|
addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]));
|
||||||
|
@ -175,6 +175,20 @@ namespace JsonDetail
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
struct JsonConverter<std::set<Type> >
|
||||||
|
{
|
||||||
|
static std::set<Type> convert(const JsonNode & node)
|
||||||
|
{
|
||||||
|
std::set<Type> ret;
|
||||||
|
BOOST_FOREACH(auto entry, node.Vector())
|
||||||
|
{
|
||||||
|
ret.insert(entry.convertTo<Type>());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
struct JsonConverter<std::vector<Type> >
|
struct JsonConverter<std::vector<Type> >
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user