mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-28 15:09:43 +00:00
Merge branch 'raceless' into 'master'
Replace missing NPC races and default animations Closes #6754 See merge request OpenMW/openmw!3703
This commit is contained in:
commit
2ff2e3c2ef
5 changed files with 50 additions and 15 deletions
|
@ -38,6 +38,7 @@
|
|||
Bug #6657: Distant terrain tiles become black when using FWIW mod
|
||||
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
|
||||
Bug #6716: mwscript comparison operator handling is too restrictive
|
||||
Bug #6754: Beast to Non-beast transformation mod is not working on OpenMW
|
||||
Bug #6807: Ultimate Galleon is not working properly
|
||||
Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands
|
||||
Bug #6894: Added item combines with equipped stack instead of creating a new unequipped stack
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "actorutil.hpp"
|
||||
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -29,4 +30,11 @@ namespace MWRender
|
|||
return Settings::models().mXbaseanim1st;
|
||||
}
|
||||
}
|
||||
|
||||
bool isDefaultActorSkeleton(std::string_view model)
|
||||
{
|
||||
return VFS::Path::pathEqual(Settings::models().mBaseanimkna.get(), model)
|
||||
|| VFS::Path::pathEqual(Settings::models().mBaseanimfemale.get(), model)
|
||||
|| VFS::Path::pathEqual(Settings::models().mBaseanim.get(), model);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
#define OPENMW_APPS_OPENMW_MWRENDER_ACTORUTIL_H
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
const std::string& getActorSkeleton(bool firstPerson, bool female, bool beast, bool werewolf);
|
||||
bool isDefaultActorSkeleton(std::string_view model);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -492,9 +492,13 @@ namespace MWRender
|
|||
getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf), mResourceSystem->getVFS());
|
||||
|
||||
std::string smodel = defaultSkeleton;
|
||||
bool isBase = !isWerewolf;
|
||||
if (!is1stPerson && !isWerewolf && !mNpc->mModel.empty())
|
||||
smodel = Misc::ResourceHelpers::correctActorModelPath(
|
||||
Misc::ResourceHelpers::correctMeshPath(mNpc->mModel), mResourceSystem->getVFS());
|
||||
{
|
||||
std::string model = Misc::ResourceHelpers::correctMeshPath(mNpc->mModel);
|
||||
isBase = isDefaultActorSkeleton(model);
|
||||
smodel = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());
|
||||
}
|
||||
|
||||
setObjectRoot(smodel, true, true, false);
|
||||
|
||||
|
@ -503,24 +507,26 @@ namespace MWRender
|
|||
if (!is1stPerson)
|
||||
{
|
||||
const std::string& base = Settings::models().mXbaseanim;
|
||||
if (smodel != base && !isWerewolf)
|
||||
if (!isWerewolf)
|
||||
addAnimSource(base, smodel);
|
||||
|
||||
if (smodel != defaultSkeleton && base != defaultSkeleton)
|
||||
addAnimSource(defaultSkeleton, smodel);
|
||||
|
||||
addAnimSource(smodel, smodel);
|
||||
if (!isBase)
|
||||
addAnimSource(smodel, smodel);
|
||||
|
||||
if (!isWerewolf && mNpc->mRace.contains("argonian"))
|
||||
if (!isWerewolf && isBeast && mNpc->mRace.contains("argonian"))
|
||||
addAnimSource("meshes\\xargonian_swimkna.nif", smodel);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string& base = Settings::models().mXbaseanim1st;
|
||||
if (smodel != base && !isWerewolf)
|
||||
if (!isWerewolf)
|
||||
addAnimSource(base, smodel);
|
||||
|
||||
addAnimSource(smodel, smodel);
|
||||
if (!isBase)
|
||||
addAnimSource(smodel, smodel);
|
||||
|
||||
mObjectRoot->setNodeMask(Mask_FirstPerson);
|
||||
mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView));
|
||||
|
|
|
@ -83,12 +83,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 +123,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 +132,15 @@ namespace
|
|||
changed = true;
|
||||
}
|
||||
|
||||
const ESM::Race* race = races.search(npc.mRace);
|
||||
if (!race)
|
||||
{
|
||||
Log(Debug::Verbose) << "NPC " << npc.mId << " (" << npc.mName << ") has nonexistent race " << npc.mRace
|
||||
<< ", using " << defaultRace << " race as replacement.";
|
||||
npc.mRace = defaultRace;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!npc.mScript.empty() && !scripts.search(npc.mScript))
|
||||
{
|
||||
Log(Debug::Verbose) << "NPC " << npc.mId << " (" << npc.mName << ") has nonexistent script "
|
||||
|
@ -580,8 +598,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 +641,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