mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 08:23:53 +00:00
Merge branch 'fix_fallout3' into 'master'
Make traits and base data optional for ESM4 NPC See merge request OpenMW/openmw!3682
This commit is contained in:
commit
ca5e7d1ccb
3 changed files with 53 additions and 36 deletions
|
@ -88,26 +88,34 @@ namespace MWClass
|
||||||
auto data = std::make_unique<ESM4NpcCustomData>();
|
auto data = std::make_unique<ESM4NpcCustomData>();
|
||||||
|
|
||||||
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
||||||
auto npcRecs = withBaseTemplates<ESM4::LevelledNpc, ESM4::Npc>(ptr.get<ESM4::Npc>()->mBase);
|
const ESM4::Npc* const base = ptr.get<ESM4::Npc>()->mBase;
|
||||||
|
auto npcRecs = withBaseTemplates<ESM4::LevelledNpc, ESM4::Npc>(base);
|
||||||
|
|
||||||
data->mTraits = chooseTemplate(npcRecs, ESM4::Npc::Template_UseTraits);
|
data->mTraits = chooseTemplate(npcRecs, ESM4::Npc::Template_UseTraits);
|
||||||
|
|
||||||
|
if (data->mTraits == nullptr)
|
||||||
|
Log(Debug::Warning) << "Traits are not found for ESM4 NPC base record: \"" << base->mEditorId << "\" ("
|
||||||
|
<< ESM::RefId(base->mId) << ")";
|
||||||
|
|
||||||
data->mBaseData = chooseTemplate(npcRecs, ESM4::Npc::Template_UseBaseData);
|
data->mBaseData = chooseTemplate(npcRecs, ESM4::Npc::Template_UseBaseData);
|
||||||
|
|
||||||
if (!data->mTraits)
|
if (data->mBaseData == nullptr)
|
||||||
throw std::runtime_error("ESM4 NPC traits not found");
|
Log(Debug::Warning) << "Base data is not found for ESM4 NPC base record: \"" << base->mEditorId << "\" ("
|
||||||
if (!data->mBaseData)
|
<< ESM::RefId(base->mId) << ")";
|
||||||
throw std::runtime_error("ESM4 NPC base data not found");
|
|
||||||
|
|
||||||
data->mRace = store->get<ESM4::Race>().find(data->mTraits->mRace);
|
if (data->mTraits != nullptr)
|
||||||
if (data->mTraits->mIsTES4)
|
{
|
||||||
data->mIsFemale = data->mTraits->mBaseConfig.tes4.flags & ESM4::Npc::TES4_Female;
|
data->mRace = store->get<ESM4::Race>().find(data->mTraits->mRace);
|
||||||
else if (data->mTraits->mIsFONV)
|
if (data->mTraits->mIsTES4)
|
||||||
data->mIsFemale = data->mTraits->mBaseConfig.fo3.flags & ESM4::Npc::FO3_Female;
|
data->mIsFemale = data->mTraits->mBaseConfig.tes4.flags & ESM4::Npc::TES4_Female;
|
||||||
else if (data->mTraits->mIsFO4)
|
else if (data->mTraits->mIsFONV)
|
||||||
data->mIsFemale
|
data->mIsFemale = data->mTraits->mBaseConfig.fo3.flags & ESM4::Npc::FO3_Female;
|
||||||
= data->mTraits->mBaseConfig.fo4.flags & ESM4::Npc::TES5_Female; // FO4 flags are the same as TES5
|
else if (data->mTraits->mIsFO4)
|
||||||
else
|
data->mIsFemale
|
||||||
data->mIsFemale = data->mTraits->mBaseConfig.tes5.flags & ESM4::Npc::TES5_Female;
|
= data->mTraits->mBaseConfig.fo4.flags & ESM4::Npc::TES5_Female; // FO4 flags are the same as TES5
|
||||||
|
else
|
||||||
|
data->mIsFemale = data->mTraits->mBaseConfig.tes5.flags & ESM4::Npc::TES5_Female;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto inv = chooseTemplate(npcRecs, ESM4::Npc::Template_UseInventory))
|
if (auto inv = chooseTemplate(npcRecs, ESM4::Npc::Template_UseInventory))
|
||||||
{
|
{
|
||||||
|
@ -116,7 +124,7 @@ namespace MWClass
|
||||||
if (auto* armor
|
if (auto* armor
|
||||||
= ESM4Impl::resolveLevelled<ESM4::LevelledItem, ESM4::Armor>(ESM::FormId::fromUint32(item.item)))
|
= ESM4Impl::resolveLevelled<ESM4::LevelledItem, ESM4::Armor>(ESM::FormId::fromUint32(item.item)))
|
||||||
data->mEquippedArmor.push_back(armor);
|
data->mEquippedArmor.push_back(armor);
|
||||||
else if (data->mTraits->mIsTES4)
|
else if (data->mTraits != nullptr && data->mTraits->mIsTES4)
|
||||||
{
|
{
|
||||||
const auto* clothing = ESM4Impl::resolveLevelled<ESM4::LevelledItem, ESM4::Clothing>(
|
const auto* clothing = ESM4Impl::resolveLevelled<ESM4::LevelledItem, ESM4::Clothing>(
|
||||||
ESM::FormId::fromUint32(item.item));
|
ESM::FormId::fromUint32(item.item));
|
||||||
|
@ -170,6 +178,8 @@ namespace MWClass
|
||||||
std::string ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
const ESM4NpcCustomData& data = getCustomData(ptr);
|
const ESM4NpcCustomData& data = getCustomData(ptr);
|
||||||
|
if (data.mTraits == nullptr)
|
||||||
|
return {};
|
||||||
std::string_view model;
|
std::string_view model;
|
||||||
if (data.mTraits->mIsTES4)
|
if (data.mTraits->mIsTES4)
|
||||||
model = data.mTraits->mModel;
|
model = data.mTraits->mModel;
|
||||||
|
@ -181,6 +191,9 @@ namespace MWClass
|
||||||
|
|
||||||
std::string_view ESM4Npc::getName(const MWWorld::ConstPtr& ptr) const
|
std::string_view ESM4Npc::getName(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getCustomData(ptr).mBaseData->mFullName;
|
const ESM4::Npc* const baseData = getCustomData(ptr).mBaseData;
|
||||||
|
if (baseData == nullptr)
|
||||||
|
return {};
|
||||||
|
return baseData->mFullName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
|
||||||
#include "../mwclass/esm4npc.hpp"
|
#include "../mwclass/esm4npc.hpp"
|
||||||
#include "../mwworld/customdata.hpp"
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
|
@ -28,11 +27,13 @@ namespace MWRender
|
||||||
|
|
||||||
void ESM4NpcAnimation::updateParts()
|
void ESM4NpcAnimation::updateParts()
|
||||||
{
|
{
|
||||||
if (!mObjectRoot.get())
|
if (mObjectRoot == nullptr)
|
||||||
return;
|
return;
|
||||||
const ESM4::Npc* traits = MWClass::ESM4Npc::getTraitsRecord(mPtr);
|
const ESM4::Npc* traits = MWClass::ESM4Npc::getTraitsRecord(mPtr);
|
||||||
|
if (traits == nullptr)
|
||||||
|
return;
|
||||||
if (traits->mIsTES4)
|
if (traits->mIsTES4)
|
||||||
updatePartsTES4();
|
updatePartsTES4(*traits);
|
||||||
else if (traits->mIsFONV)
|
else if (traits->mIsFONV)
|
||||||
{
|
{
|
||||||
// Not implemented yet
|
// Not implemented yet
|
||||||
|
@ -42,7 +43,7 @@ namespace MWRender
|
||||||
// There is no easy way to distinguish TES5 and FO3.
|
// There is no easy way to distinguish TES5 and FO3.
|
||||||
// In case of FO3 the function shouldn't crash the game and will
|
// In case of FO3 the function shouldn't crash the game and will
|
||||||
// only lead to the NPC not being rendered.
|
// only lead to the NPC not being rendered.
|
||||||
updatePartsTES5();
|
updatePartsTES5(*traits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +66,8 @@ namespace MWRender
|
||||||
return rec->mModel;
|
return rec->mModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM4NpcAnimation::updatePartsTES4()
|
void ESM4NpcAnimation::updatePartsTES4(const ESM4::Npc& traits)
|
||||||
{
|
{
|
||||||
const ESM4::Npc* traits = MWClass::ESM4Npc::getTraitsRecord(mPtr);
|
|
||||||
const ESM4::Race* race = MWClass::ESM4Npc::getRace(mPtr);
|
const ESM4::Race* race = MWClass::ESM4Npc::getRace(mPtr);
|
||||||
bool isFemale = MWClass::ESM4Npc::isFemale(mPtr);
|
bool isFemale = MWClass::ESM4Npc::isFemale(mPtr);
|
||||||
|
|
||||||
|
@ -77,13 +77,13 @@ namespace MWRender
|
||||||
insertPart(bodyPart.mesh);
|
insertPart(bodyPart.mesh);
|
||||||
for (const ESM4::Race::BodyPart& bodyPart : race->mHeadParts)
|
for (const ESM4::Race::BodyPart& bodyPart : race->mHeadParts)
|
||||||
insertPart(bodyPart.mesh);
|
insertPart(bodyPart.mesh);
|
||||||
if (!traits->mHair.isZeroOrUnset())
|
if (!traits.mHair.isZeroOrUnset())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
||||||
if (const ESM4::Hair* hair = store->get<ESM4::Hair>().search(traits->mHair))
|
if (const ESM4::Hair* hair = store->get<ESM4::Hair>().search(traits.mHair))
|
||||||
insertPart(hair->mModel);
|
insertPart(hair->mModel);
|
||||||
else
|
else
|
||||||
Log(Debug::Error) << "Hair not found: " << ESM::RefId(traits->mHair);
|
Log(Debug::Error) << "Hair not found: " << ESM::RefId(traits.mHair);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ESM4::Armor* armor : MWClass::ESM4Npc::getEquippedArmor(mPtr))
|
for (const ESM4::Armor* armor : MWClass::ESM4Npc::getEquippedArmor(mPtr))
|
||||||
|
@ -111,11 +111,10 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM4NpcAnimation::updatePartsTES5()
|
void ESM4NpcAnimation::updatePartsTES5(const ESM4::Npc& traits)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
||||||
|
|
||||||
const ESM4::Npc* traits = MWClass::ESM4Npc::getTraitsRecord(mPtr);
|
|
||||||
const ESM4::Race* race = MWClass::ESM4Npc::getRace(mPtr);
|
const ESM4::Race* race = MWClass::ESM4Npc::getRace(mPtr);
|
||||||
bool isFemale = MWClass::ESM4Npc::isFemale(mPtr);
|
bool isFemale = MWClass::ESM4Npc::isFemale(mPtr);
|
||||||
|
|
||||||
|
@ -132,9 +131,9 @@ namespace MWRender
|
||||||
Log(Debug::Error) << "ArmorAddon not found: " << ESM::RefId(armaId);
|
Log(Debug::Error) << "ArmorAddon not found: " << ESM::RefId(armaId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bool compatibleRace = arma->mRacePrimary == traits->mRace;
|
bool compatibleRace = arma->mRacePrimary == traits.mRace;
|
||||||
for (auto r : arma->mRaces)
|
for (auto r : arma->mRaces)
|
||||||
if (r == traits->mRace)
|
if (r == traits.mRace)
|
||||||
compatibleRace = true;
|
compatibleRace = true;
|
||||||
if (compatibleRace)
|
if (compatibleRace)
|
||||||
armorAddons.push_back(arma);
|
armorAddons.push_back(arma);
|
||||||
|
@ -143,12 +142,12 @@ namespace MWRender
|
||||||
|
|
||||||
for (const ESM4::Armor* armor : MWClass::ESM4Npc::getEquippedArmor(mPtr))
|
for (const ESM4::Armor* armor : MWClass::ESM4Npc::getEquippedArmor(mPtr))
|
||||||
findArmorAddons(armor);
|
findArmorAddons(armor);
|
||||||
if (!traits->mWornArmor.isZeroOrUnset())
|
if (!traits.mWornArmor.isZeroOrUnset())
|
||||||
{
|
{
|
||||||
if (const ESM4::Armor* armor = store->get<ESM4::Armor>().search(traits->mWornArmor))
|
if (const ESM4::Armor* armor = store->get<ESM4::Armor>().search(traits.mWornArmor))
|
||||||
findArmorAddons(armor);
|
findArmorAddons(armor);
|
||||||
else
|
else
|
||||||
Log(Debug::Error) << "Worn armor not found: " << ESM::RefId(traits->mWornArmor);
|
Log(Debug::Error) << "Worn armor not found: " << ESM::RefId(traits.mWornArmor);
|
||||||
}
|
}
|
||||||
if (!race->mSkin.isZeroOrUnset())
|
if (!race->mSkin.isZeroOrUnset())
|
||||||
{
|
{
|
||||||
|
@ -183,7 +182,7 @@ namespace MWRender
|
||||||
std::set<uint32_t> usedHeadPartTypes;
|
std::set<uint32_t> usedHeadPartTypes;
|
||||||
if (usedParts & ESM4::Armor::TES5_Hair)
|
if (usedParts & ESM4::Armor::TES5_Hair)
|
||||||
usedHeadPartTypes.insert(ESM4::HeadPart::Type_Hair);
|
usedHeadPartTypes.insert(ESM4::HeadPart::Type_Hair);
|
||||||
insertHeadParts(traits->mHeadParts, usedHeadPartTypes);
|
insertHeadParts(traits.mHeadParts, usedHeadPartTypes);
|
||||||
insertHeadParts(isFemale ? race->mHeadPartIdsFemale : race->mHeadPartIdsMale, usedHeadPartTypes);
|
insertHeadParts(isFemale ? race->mHeadPartIdsFemale : race->mHeadPartIdsMale, usedHeadPartTypes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
|
|
||||||
#include "animation.hpp"
|
#include "animation.hpp"
|
||||||
|
|
||||||
|
namespace ESM4
|
||||||
|
{
|
||||||
|
struct Npc;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class ESM4NpcAnimation : public Animation
|
class ESM4NpcAnimation : public Animation
|
||||||
|
@ -18,8 +23,8 @@ namespace MWRender
|
||||||
void insertHeadParts(const std::vector<ESM::FormId>& partIds, std::set<uint32_t>& usedHeadPartTypes);
|
void insertHeadParts(const std::vector<ESM::FormId>& partIds, std::set<uint32_t>& usedHeadPartTypes);
|
||||||
|
|
||||||
void updateParts();
|
void updateParts();
|
||||||
void updatePartsTES4();
|
void updatePartsTES4(const ESM4::Npc& traits);
|
||||||
void updatePartsTES5();
|
void updatePartsTES5(const ESM4::Npc& traits);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue