mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-23 03:47:34 -04:00
update
This commit is contained in:
parent
b4552d47f6
commit
3882a5f25a
@ -34,11 +34,10 @@ namespace MWClass
|
|||||||
|
|
||||||
static const ESM4::Npc* chooseTemplate(const std::vector<const ESM4::Npc*>& recs, uint16_t flag)
|
static const ESM4::Npc* chooseTemplate(const std::vector<const ESM4::Npc*>& recs, uint16_t flag)
|
||||||
{
|
{
|
||||||
// If the record is neither TES4 nor TES5 (though maybe FO4 is compatible with tes5.templateFlags), then
|
// In case of FO3 the function may return nullptr that will lead to "ESM4 NPC traits not found"
|
||||||
// the function can return nullptr that will lead to "ESM4 NPC traits not found" exception and the NPC
|
// exception and the NPC will not be added to the scene. But in any way it shouldn't cause a crash.
|
||||||
// will not be added to the scene. But in any way it shouldn't cause a crash.
|
|
||||||
for (const auto* rec : recs)
|
for (const auto* rec : recs)
|
||||||
if (rec->mIsTES4 || !(rec->mBaseConfig.tes5.templateFlags & flag))
|
if (rec->mIsTES4 || rec->mIsFONV || !(rec->mBaseConfig.tes5.templateFlags & flag))
|
||||||
return rec;
|
return rec;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -59,9 +58,16 @@ namespace MWClass
|
|||||||
const ESM4NpcCustomData& asESM4NpcCustomData() const override { return *this; }
|
const ESM4NpcCustomData& asESM4NpcCustomData() const override { return *this; }
|
||||||
};
|
};
|
||||||
|
|
||||||
ESM4NpcCustomData& ESM4Npc::getCustomData(const MWWorld::Ptr& ptr)
|
ESM4NpcCustomData& ESM4Npc::getCustomData(const MWWorld::ConstPtr& ptr)
|
||||||
{
|
{
|
||||||
if (auto* data = ptr.getRefData().getCustomData())
|
// Note: the argument is ConstPtr because this function is used in `getModel` and `getName`
|
||||||
|
// which are virtual and work with ConstPtr. `getModel` and `getName` use custom data
|
||||||
|
// because they require a lot of work including levelled records resolving and it would be
|
||||||
|
// stupid to not to cache the results. Maybe we should stop using ConstPtr at all
|
||||||
|
// to avoid such workarounds.
|
||||||
|
MWWorld::RefData& refData = const_cast<MWWorld::RefData&>(ptr.getRefData());
|
||||||
|
|
||||||
|
if (auto* data = refData.getCustomData())
|
||||||
return data->asESM4NpcCustomData();
|
return data->asESM4NpcCustomData();
|
||||||
|
|
||||||
auto data = std::make_unique<ESM4NpcCustomData>();
|
auto data = std::make_unique<ESM4NpcCustomData>();
|
||||||
@ -114,7 +120,7 @@ namespace MWClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
ESM4NpcCustomData& res = *data;
|
ESM4NpcCustomData& res = *data;
|
||||||
ptr.getRefData().setCustomData(std::move(data));
|
refData.setCustomData(std::move(data));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,23 +151,18 @@ namespace MWClass
|
|||||||
|
|
||||||
std::string ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
const ESM4NpcCustomData& data = getCustomData(ptr);
|
||||||
return "";
|
std::string_view model;
|
||||||
const ESM4NpcCustomData& data = ptr.getRefData().getCustomData()->asESM4NpcCustomData();
|
|
||||||
const VFS::Manager* vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
|
||||||
if (data.mTraits->mIsTES4)
|
if (data.mTraits->mIsTES4)
|
||||||
return Misc::ResourceHelpers::correctMeshPath(data.mTraits->mModel, vfs);
|
model = data.mTraits->mModel;
|
||||||
if (data.mIsFemale)
|
|
||||||
return Misc::ResourceHelpers::correctMeshPath(data.mRace->mModelFemale, vfs);
|
|
||||||
else
|
else
|
||||||
return Misc::ResourceHelpers::correctMeshPath(data.mRace->mModelMale, vfs);
|
model = data.mIsFemale ? data.mRace->mModelFemale : data.mRace->mModelMale;
|
||||||
|
const VFS::Manager* vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||||
|
return Misc::ResourceHelpers::correctMeshPath(model, vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view ESM4Npc::getName(const MWWorld::ConstPtr& ptr) const
|
std::string_view ESM4Npc::getName(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
if (!ptr.getRefData().getCustomData())
|
return getCustomData(ptr).mBaseData->mFullName;
|
||||||
return "";
|
|
||||||
const ESM4NpcCustomData& data = ptr.getRefData().getCustomData()->asESM4NpcCustomData();
|
|
||||||
return data.mBaseData->mFullName;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ namespace MWClass
|
|||||||
static const std::vector<const ESM4::Clothing*>& getEquippedClothing(const MWWorld::Ptr& ptr);
|
static const std::vector<const ESM4::Clothing*>& getEquippedClothing(const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static ESM4NpcCustomData& getCustomData(const MWWorld::Ptr& ptr);
|
static ESM4NpcCustomData& getCustomData(const MWWorld::ConstPtr& ptr);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,13 +31,19 @@ namespace MWRender
|
|||||||
if (!mObjectRoot.get())
|
if (!mObjectRoot.get())
|
||||||
return;
|
return;
|
||||||
const ESM4::Npc* traits = MWClass::ESM4Npc::getTraitsRecord(mPtr);
|
const ESM4::Npc* traits = MWClass::ESM4Npc::getTraitsRecord(mPtr);
|
||||||
// There is no flag "mIsTES5", so we can not distinguish from other cases.
|
|
||||||
// But calling wrong `updateParts*` function shouldn't crash the game and will
|
|
||||||
// only lead to the NPC not being rendered.
|
|
||||||
if (traits->mIsTES4)
|
if (traits->mIsTES4)
|
||||||
updatePartsTES4();
|
updatePartsTES4();
|
||||||
|
else if (traits->mIsFONV)
|
||||||
|
{
|
||||||
|
// Not implemented yet
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// There is no easy way to distinguish TES5 and FO3.
|
||||||
|
// In case of FO3 the function shouldn't crash the game and will
|
||||||
|
// only lead to the NPC not being rendered.
|
||||||
updatePartsTES5();
|
updatePartsTES5();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM4NpcAnimation::insertPart(std::string_view model)
|
void ESM4NpcAnimation::insertPart(std::string_view model)
|
||||||
@ -76,6 +82,8 @@ namespace MWRender
|
|||||||
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
|
||||||
|
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))
|
||||||
@ -84,6 +92,25 @@ namespace MWRender
|
|||||||
insertPart(chooseTes4EquipmentModel(clothing, isFemale));
|
insertPart(chooseTes4EquipmentModel(clothing, isFemale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESM4NpcAnimation::insertHeadParts(
|
||||||
|
const std::vector<ESM::FormId>& partIds, std::set<uint32_t>& usedHeadPartTypes)
|
||||||
|
{
|
||||||
|
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
||||||
|
for (ESM::FormId partId : partIds)
|
||||||
|
{
|
||||||
|
if (partId.isZeroOrUnset())
|
||||||
|
continue;
|
||||||
|
const ESM4::HeadPart* part = store->get<ESM4::HeadPart>().search(partId);
|
||||||
|
if (!part)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Head part not found: " << ESM::RefId(partId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (usedHeadPartTypes.emplace(part->mType).second)
|
||||||
|
insertPart(part->mModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ESM4NpcAnimation::updatePartsTES5()
|
void ESM4NpcAnimation::updatePartsTES5()
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore();
|
||||||
@ -92,23 +119,6 @@ namespace MWRender
|
|||||||
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);
|
||||||
|
|
||||||
std::set<uint32_t> usedHeadPartTypes;
|
|
||||||
auto addHeadParts = [&](const std::vector<ESM::FormId>& partIds) {
|
|
||||||
for (ESM::FormId partId : partIds)
|
|
||||||
{
|
|
||||||
if (partId.isZeroOrUnset())
|
|
||||||
continue;
|
|
||||||
const ESM4::HeadPart* part = store->get<ESM4::HeadPart>().search(partId);
|
|
||||||
if (!part)
|
|
||||||
{
|
|
||||||
Log(Debug::Error) << "Head part not found: " << ESM::RefId(partId);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (usedHeadPartTypes.emplace(part->mType).second)
|
|
||||||
insertPart(part->mModel);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<const ESM4::ArmorAddon*> armorAddons;
|
std::vector<const ESM4::ArmorAddon*> armorAddons;
|
||||||
|
|
||||||
auto findArmorAddons = [&](const ESM4::Armor* armor) {
|
auto findArmorAddons = [&](const ESM4::Armor* armor) {
|
||||||
@ -132,9 +142,19 @@ 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())
|
||||||
findArmorAddons(store->get<ESM4::Armor>().find(traits->mWornArmor));
|
{
|
||||||
|
if (const ESM4::Armor* armor = store->get<ESM4::Armor>().search(traits->mWornArmor))
|
||||||
|
findArmorAddons(armor);
|
||||||
|
else
|
||||||
|
Log(Debug::Error) << "Worn armor not found: " << ESM::RefId(traits->mWornArmor);
|
||||||
|
}
|
||||||
if (!race->mSkin.isZeroOrUnset())
|
if (!race->mSkin.isZeroOrUnset())
|
||||||
findArmorAddons(store->get<ESM4::Armor>().find(race->mSkin));
|
{
|
||||||
|
if (const ESM4::Armor* armor = store->get<ESM4::Armor>().search(race->mSkin))
|
||||||
|
findArmorAddons(armor);
|
||||||
|
else
|
||||||
|
Log(Debug::Error) << "Skin not found: " << ESM::RefId(race->mSkin);
|
||||||
|
}
|
||||||
|
|
||||||
if (isFemale)
|
if (isFemale)
|
||||||
std::sort(armorAddons.begin(), armorAddons.end(),
|
std::sort(armorAddons.begin(), armorAddons.end(),
|
||||||
@ -158,9 +178,10 @@ namespace MWRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
addHeadParts(traits->mHeadParts);
|
insertHeadParts(traits->mHeadParts, usedHeadPartTypes);
|
||||||
addHeadParts(isFemale ? race->mHeadPartIdsFemale : race->mHeadPartIdsMale);
|
insertHeadParts(isFemale ? race->mHeadPartIdsFemale : race->mHeadPartIdsMale, usedHeadPartTypes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ namespace MWRender
|
|||||||
private:
|
private:
|
||||||
void insertPart(std::string_view model);
|
void insertPart(std::string_view model);
|
||||||
|
|
||||||
|
// Works for FO3/FONV/TES5
|
||||||
|
void insertHeadParts(const std::vector<ESM::FormId>& partIds, std::set<uint32_t>& usedHeadPartTypes);
|
||||||
|
|
||||||
void updateParts();
|
void updateParts();
|
||||||
void updatePartsTES4();
|
void updatePartsTES4();
|
||||||
void updatePartsTES5();
|
void updatePartsTES5();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user