mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-31 20:45:34 +00:00
Replace missing NPC races and default animations
This commit is contained in:
parent
78459314bf
commit
5bd5c84018
1 changed files with 54 additions and 8 deletions
|
@ -17,8 +17,10 @@
|
|||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
#include <components/lua/configuration.hpp>
|
||||
#include <components/misc/algorithm.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwmechanics/spelllist.hpp"
|
||||
#include "../mwrender/actorutil.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -83,12 +85,22 @@ namespace
|
|||
throw std::runtime_error("List of NPC classes is empty!");
|
||||
}
|
||||
|
||||
const ESM::RefId& getDefaultRace(const MWWorld::Store<ESM::Race>& races)
|
||||
{
|
||||
auto it = races.begin();
|
||||
if (it != races.end())
|
||||
return it->mId;
|
||||
throw std::runtime_error("List of NPC races is empty!");
|
||||
}
|
||||
|
||||
std::vector<ESM::NPC> getNPCsToReplace(const MWWorld::Store<ESM::Faction>& factions,
|
||||
const MWWorld::Store<ESM::Class>& classes, const MWWorld::Store<ESM::Script>& scripts,
|
||||
const std::unordered_map<ESM::RefId, ESM::NPC>& npcs)
|
||||
const MWWorld::Store<ESM::Class>& classes, const MWWorld::Store<ESM::Race>& races,
|
||||
const MWWorld::Store<ESM::Script>& scripts, const std::unordered_map<ESM::RefId, ESM::NPC>& npcs)
|
||||
{
|
||||
// Cache first class from store - we will use it if current class is not found
|
||||
const ESM::RefId& defaultCls = getDefaultClass(classes);
|
||||
// Same for races
|
||||
const ESM::RefId& defaultRace = getDefaultRace(races);
|
||||
|
||||
// Validate NPCs for non-existing class and faction.
|
||||
// We will replace invalid entries by fixed ones
|
||||
|
@ -113,8 +125,7 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
const ESM::RefId& npcClass = npc.mClass;
|
||||
const ESM::Class* cls = classes.search(npcClass);
|
||||
const ESM::Class* cls = classes.search(npc.mClass);
|
||||
if (!cls)
|
||||
{
|
||||
Log(Debug::Verbose) << "NPC " << npc.mId << " (" << npc.mName << ") has nonexistent class "
|
||||
|
@ -123,6 +134,41 @@ namespace
|
|||
changed = true;
|
||||
}
|
||||
|
||||
const ESM::Race* race = races.search(npc.mRace);
|
||||
if (race)
|
||||
{
|
||||
// TESCS sometimes writes the default animation nif to the animation subrecord. This harmless (as it
|
||||
// will match the NPC's race) until the NPC's race is changed. If the player record contains a default
|
||||
// non-beast race animation and the player selects a beast race in chargen, animations aren't applied
|
||||
// properly. Morrowind.exe handles this gracefully, so we clear the animation here to force the default.
|
||||
if (!npc.mModel.empty() && npc.mId == "player")
|
||||
{
|
||||
const bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||
const std::string& defaultModel = MWRender::getActorSkeleton(false, !npc.isMale(), isBeast, false);
|
||||
std::string model = Misc::ResourceHelpers::correctMeshPath(npc.mModel);
|
||||
if (model.size() == defaultModel.size())
|
||||
{
|
||||
std::replace(model.begin(), model.end(), '/', '\\');
|
||||
std::string normalizedDefault = defaultModel;
|
||||
std::replace(normalizedDefault.begin(), normalizedDefault.end(), '/', '\\');
|
||||
if (Misc::StringUtils::ciEqual(normalizedDefault, model))
|
||||
{
|
||||
npc.mModel.clear();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(Debug::Verbose) << "NPC " << npc.mId << " (" << npc.mName << ") has nonexistent race " << npc.mRace
|
||||
<< ", using " << defaultRace << " race as replacement.";
|
||||
npc.mRace = defaultRace;
|
||||
// Remove animations that might be race specific
|
||||
npc.mModel.clear();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!npc.mScript.empty() && !scripts.search(npc.mScript))
|
||||
{
|
||||
Log(Debug::Verbose) << "NPC " << npc.mId << " (" << npc.mName << ") has nonexistent script "
|
||||
|
@ -580,8 +626,8 @@ namespace MWWorld
|
|||
void ESMStore::validate()
|
||||
{
|
||||
auto& npcs = getWritable<ESM::NPC>();
|
||||
std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(
|
||||
getWritable<ESM::Faction>(), getWritable<ESM::Class>(), getWritable<ESM::Script>(), npcs.mStatic);
|
||||
std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(getWritable<ESM::Faction>(), getWritable<ESM::Class>(),
|
||||
getWritable<ESM::Race>(), getWritable<ESM::Script>(), npcs.mStatic);
|
||||
|
||||
for (const ESM::NPC& npc : npcsToReplace)
|
||||
{
|
||||
|
@ -623,8 +669,8 @@ namespace MWWorld
|
|||
auto& npcs = getWritable<ESM::NPC>();
|
||||
auto& scripts = getWritable<ESM::Script>();
|
||||
|
||||
std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(
|
||||
getWritable<ESM::Faction>(), getWritable<ESM::Class>(), getWritable<ESM::Script>(), npcs.mDynamic);
|
||||
std::vector<ESM::NPC> npcsToReplace = getNPCsToReplace(getWritable<ESM::Faction>(), getWritable<ESM::Class>(),
|
||||
getWritable<ESM::Race>(), getWritable<ESM::Script>(), npcs.mDynamic);
|
||||
|
||||
for (const ESM::NPC& npc : npcsToReplace)
|
||||
npcs.insert(npc);
|
||||
|
|
Loading…
Reference in a new issue