2016-10-08 03:49:50 +02:00
|
|
|
#include "actoranimation.hpp"
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <osg/Group>
|
|
|
|
#include <osg/Node>
|
|
|
|
#include <osg/Vec4f>
|
|
|
|
|
2022-09-08 21:08:59 +02:00
|
|
|
#include <components/esm3/loadbody.hpp>
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/loadcell.hpp>
|
|
|
|
#include <components/esm3/loadligh.hpp>
|
2016-10-08 03:49:50 +02:00
|
|
|
|
2018-03-03 14:16:21 +04:00
|
|
|
#include <components/resource/resourcesystem.hpp>
|
|
|
|
#include <components/resource/scenemanager.hpp>
|
|
|
|
|
2021-11-02 18:01:22 +01:00
|
|
|
#include <components/sceneutil/attach.hpp>
|
2023-02-07 11:30:57 +01:00
|
|
|
#include <components/sceneutil/lightcommon.hpp>
|
2016-10-08 03:49:50 +02:00
|
|
|
#include <components/sceneutil/lightmanager.hpp>
|
|
|
|
#include <components/sceneutil/lightutil.hpp>
|
2018-03-03 14:16:21 +04:00
|
|
|
#include <components/sceneutil/visitor.hpp>
|
2016-10-08 03:49:50 +02:00
|
|
|
|
2022-06-29 00:32:11 +02:00
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2023-06-27 23:41:06 +02:00
|
|
|
#include <components/settings/values.hpp>
|
2018-03-03 14:16:21 +04:00
|
|
|
|
|
|
|
#include <components/vfs/manager.hpp>
|
|
|
|
|
2016-10-08 03:49:50 +02:00
|
|
|
#include "../mwbase/environment.hpp"
|
|
|
|
#include "../mwmechanics/actorutil.hpp"
|
|
|
|
#include "../mwmechanics/creaturestats.hpp"
|
|
|
|
#include "../mwmechanics/drawstate.hpp"
|
2018-12-26 13:45:28 +04:00
|
|
|
#include "../mwmechanics/weapontype.hpp"
|
2016-10-08 03:49:50 +02:00
|
|
|
#include "../mwworld/cellstore.hpp"
|
|
|
|
#include "../mwworld/class.hpp"
|
2020-01-10 13:00:57 +03:00
|
|
|
#include "../mwworld/esmstore.hpp"
|
2022-08-17 18:44:04 +02:00
|
|
|
#include "../mwworld/inventorystore.hpp"
|
2016-10-11 22:15:51 +02:00
|
|
|
#include "../mwworld/ptr.hpp"
|
2016-10-08 03:49:50 +02:00
|
|
|
|
2020-04-20 18:47:14 +02:00
|
|
|
#include "vismask.hpp"
|
|
|
|
|
2016-10-08 03:49:50 +02:00
|
|
|
namespace MWRender
|
|
|
|
{
|
|
|
|
|
2017-02-22 14:54:40 +01:00
|
|
|
ActorAnimation::ActorAnimation(
|
|
|
|
const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem)
|
2023-07-29 12:58:27 +04:00
|
|
|
: Animation(ptr, std::move(parentNode), resourceSystem)
|
2016-10-08 03:49:50 +02:00
|
|
|
{
|
|
|
|
MWWorld::ContainerStore& store = mPtr.getClass().getContainerStore(mPtr);
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2016-10-08 03:49:50 +02:00
|
|
|
for (MWWorld::ConstContainerStoreIterator iter = store.cbegin(MWWorld::ContainerStore::Type_Light);
|
|
|
|
iter != store.cend(); ++iter)
|
|
|
|
{
|
|
|
|
const ESM::Light* light = iter->get<ESM::Light>()->mBase;
|
|
|
|
if (!(light->mData.mFlags & ESM::Light::Carry))
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2016-10-08 03:49:50 +02:00
|
|
|
addHiddenItemLight(*iter, light);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2016-10-08 03:49:50 +02:00
|
|
|
}
|
2018-09-28 13:57:13 +04:00
|
|
|
|
|
|
|
// Make sure we cleaned object from effects, just in cast if we re-use node
|
|
|
|
removeEffects();
|
2016-10-08 03:49:50 +02:00
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-10-02 13:50:41 +02:00
|
|
|
ActorAnimation::~ActorAnimation()
|
|
|
|
{
|
|
|
|
removeFromSceneImpl();
|
|
|
|
}
|
2021-09-12 07:56:20 +00:00
|
|
|
|
|
|
|
PartHolderPtr ActorAnimation::attachMesh(
|
|
|
|
const std::string& model, std::string_view bonename, bool enchantedGlow, osg::Vec4f* glowColor)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2021-09-12 07:56:20 +00:00
|
|
|
osg::Group* parent = getBoneByName(bonename);
|
|
|
|
if (!parent)
|
|
|
|
return nullptr;
|
|
|
|
|
refactors a case insensitive map (#3184)
This PR aims to spark the retirement of a questionable pattern I have found all over our code base. I will illustrate how this pattern encourages code duplication, lacks type safety, requires documentation and can be prone to bugs.
```
std::map<std::string, Object> mMap; // Stored in all lowercase for a case-insensitive lookup
std::string lowerKey = Misc::StringUtils::lowerCase(key);
mMap.emplace(lowerKey, object);
std::string lowerKey = Misc::StringUtils::lowerCase(key);
mMap.find(lowerKey);
mMap.find(key); // Not found. Oops!
```
An alternative approach produces no such issues.
```
std::unordered_map<std::string, Object, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> mMap;
mMap.emplace(key, object);
mMap.find(key);
```
Of course, such an alternative will work for a `map` as well, but an `unordered_map` is generally preferable over a `map` with these changes because we have moved `lowerCase` into the comparison operator.
In this PR I have refactored `Animation::mNodeMap` accordingly. I have reviewed and adapted all direct and indirect usage of `Animation::mNodeMap` to ensure we do not change behaviour with this PR.
2021-10-25 07:18:26 +00:00
|
|
|
osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->getInstance(model, parent);
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2019-08-07 11:03:26 +04:00
|
|
|
const NodeMap& nodeMap = getNodeMap();
|
|
|
|
NodeMap::const_iterator found = nodeMap.find(bonename);
|
|
|
|
if (found == nodeMap.end())
|
|
|
|
return {};
|
2018-03-03 14:16:21 +04:00
|
|
|
|
|
|
|
if (enchantedGlow)
|
2022-08-21 18:53:38 +02:00
|
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(instance, mResourceSystem, *glowColor);
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-08-21 18:53:38 +02:00
|
|
|
return std::make_unique<PartHolder>(instance);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2021-11-02 18:01:22 +01:00
|
|
|
|
2022-08-21 18:53:38 +02:00
|
|
|
osg::ref_ptr<osg::Node> ActorAnimation::attach(
|
|
|
|
const std::string& model, std::string_view bonename, std::string_view bonefilter, bool isLight)
|
2021-11-02 18:01:22 +01:00
|
|
|
{
|
|
|
|
osg::ref_ptr<const osg::Node> templateNode = mResourceSystem->getSceneManager()->getTemplate(model);
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2021-11-02 18:01:22 +01:00
|
|
|
const NodeMap& nodeMap = getNodeMap();
|
|
|
|
auto found = nodeMap.find(bonename);
|
|
|
|
if (found == nodeMap.end())
|
2022-08-21 18:53:38 +02:00
|
|
|
throw std::runtime_error("Can't find attachment node " + std::string{ bonename });
|
2021-11-02 18:01:22 +01:00
|
|
|
if (isLight)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2021-11-02 18:01:22 +01:00
|
|
|
osg::Quat rotation(osg::DegreesToRadians(-90.f), osg::Vec3f(1, 0, 0));
|
|
|
|
return SceneUtil::attach(
|
|
|
|
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager(), &rotation);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2021-11-02 18:01:22 +01:00
|
|
|
return SceneUtil::attach(
|
|
|
|
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager());
|
|
|
|
}
|
|
|
|
|
2021-11-01 19:33:29 +01:00
|
|
|
std::string ActorAnimation::getShieldMesh(const MWWorld::ConstPtr& shield, bool female) const
|
2020-01-10 13:00:57 +03:00
|
|
|
{
|
|
|
|
const ESM::Armor* armor = shield.get<ESM::Armor>()->mBase;
|
|
|
|
const std::vector<ESM::PartReference>& bodyparts = armor->mParts.mParts;
|
|
|
|
// Try to recover the body part model, use ground model as a fallback otherwise.
|
|
|
|
if (!bodyparts.empty())
|
|
|
|
{
|
2023-04-20 21:07:53 +02:00
|
|
|
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
2021-11-01 19:33:29 +01:00
|
|
|
const MWWorld::Store<ESM::BodyPart>& partStore = store.get<ESM::BodyPart>();
|
|
|
|
for (const auto& part : bodyparts)
|
2020-01-10 13:00:57 +03:00
|
|
|
{
|
2021-11-01 19:33:29 +01:00
|
|
|
if (part.mPart != ESM::PRT_Shield)
|
2020-01-10 13:00:57 +03:00
|
|
|
continue;
|
2022-09-22 21:26:05 +03:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId* bodypartName = nullptr;
|
2021-11-01 19:33:29 +01:00
|
|
|
if (female && !part.mFemale.empty())
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
bodypartName = &part.mFemale;
|
2021-11-01 19:33:29 +01:00
|
|
|
else if (!part.mMale.empty())
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
bodypartName = &part.mMale;
|
2022-09-22 21:26:05 +03:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
if (bodypartName && !bodypartName->empty())
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::BodyPart* bodypart = partStore.search(*bodypartName);
|
2021-11-01 19:33:29 +01:00
|
|
|
if (bodypart == nullptr || bodypart->mData.mType != ESM::BodyPart::MT_Armor)
|
|
|
|
return std::string();
|
|
|
|
if (!bodypart->mModel.empty())
|
2023-12-26 13:00:30 +01:00
|
|
|
return Misc::ResourceHelpers::correctMeshPath(bodypart->mModel);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2020-01-10 13:00:57 +03:00
|
|
|
}
|
|
|
|
}
|
2021-11-01 19:33:29 +01:00
|
|
|
return shield.getClass().getModel(shield);
|
2020-01-10 13:00:57 +03:00
|
|
|
}
|
2021-11-01 19:33:29 +01:00
|
|
|
|
|
|
|
std::string ActorAnimation::getSheathedShieldMesh(const MWWorld::ConstPtr& shield) const
|
|
|
|
{
|
|
|
|
std::string mesh = getShieldMesh(shield, false);
|
2020-01-10 13:00:57 +03:00
|
|
|
|
|
|
|
if (mesh.empty())
|
|
|
|
return mesh;
|
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
std::string holsteredName = mesh;
|
|
|
|
holsteredName = holsteredName.replace(holsteredName.size() - 4, 4, "_sh.nif");
|
|
|
|
if (mResourceSystem->getVFS()->exists(holsteredName))
|
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::Node> shieldTemplate = mResourceSystem->getSceneManager()->getInstance(holsteredName);
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor("Bip01 Sheath");
|
|
|
|
shieldTemplate->accept(findVisitor);
|
|
|
|
osg::ref_ptr<osg::Node> sheathNode = findVisitor.mFoundNode;
|
|
|
|
if (!sheathNode)
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
return mesh;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ActorAnimation::updateCarriedLeftVisible(const int weaptype) const
|
|
|
|
{
|
2023-06-27 23:41:06 +02:00
|
|
|
if (Settings::game().mShieldSheathing)
|
2019-08-11 15:01:48 +04:00
|
|
|
{
|
2021-09-12 07:56:20 +00:00
|
|
|
const MWWorld::Class& cls = mPtr.getClass();
|
2019-08-11 15:01:48 +04:00
|
|
|
MWMechanics::CreatureStats& stats = cls.getCreatureStats(mPtr);
|
2021-09-12 07:56:20 +00:00
|
|
|
if (cls.hasInventoryStore(mPtr) && stats.getDrawState() == MWMechanics::DrawState::Nothing)
|
2019-08-11 15:01:48 +04:00
|
|
|
{
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor("Bip01 AttachShield");
|
2021-09-12 07:56:20 +00:00
|
|
|
mObjectRoot->accept(findVisitor);
|
2022-08-04 05:32:10 +03:00
|
|
|
if (findVisitor.mFoundNode)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-10-26 12:41:16 +04:00
|
|
|
const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
|
|
|
|
const MWWorld::ConstContainerStoreIterator shield
|
|
|
|
= inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
2021-11-01 19:33:29 +01:00
|
|
|
if (shield != inv.end() && shield->getType() == ESM::Armor::sRecordId
|
|
|
|
&& !getSheathedShieldMesh(*shield).empty())
|
2022-08-04 05:32:10 +03:00
|
|
|
return false;
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2019-08-11 15:01:48 +04:00
|
|
|
}
|
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
return !(MWMechanics::getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActorAnimation::updateHolsteredShield(bool showCarriedLeft)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2023-06-27 23:41:06 +02:00
|
|
|
if (!Settings::game().mShieldSheathing)
|
2019-08-11 15:01:48 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!mPtr.getClass().hasInventoryStore(mPtr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
mHolsteredShield.reset();
|
|
|
|
|
|
|
|
if (showCarriedLeft)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
|
|
if (shield == inv.end() || shield->getType() != ESM::Armor::sRecordId)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Can not show holdstered shields with two-handed weapons at all
|
|
|
|
const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
2021-10-11 11:46:21 +00:00
|
|
|
if (weapon == inv.end())
|
2019-08-11 15:01:48 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
auto type = weapon->getType();
|
|
|
|
if (type == ESM::Weapon::sRecordId)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-08-11 15:01:48 +04:00
|
|
|
const MWWorld::LiveCellRef<ESM::Weapon>* ref = weapon->get<ESM::Weapon>();
|
|
|
|
ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;
|
|
|
|
if (MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded)
|
|
|
|
return;
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2019-08-11 15:01:48 +04:00
|
|
|
|
|
|
|
std::string mesh = getSheathedShieldMesh(*shield);
|
|
|
|
if (mesh.empty())
|
|
|
|
return;
|
|
|
|
|
2021-11-01 19:33:29 +01:00
|
|
|
std::string_view boneName = "Bip01 AttachShield";
|
|
|
|
osg::Vec4f glowColor = shield->getClass().getEnchantmentColor(*shield);
|
|
|
|
std::string holsteredName = mesh;
|
2019-08-11 15:01:48 +04:00
|
|
|
holsteredName = holsteredName.replace(holsteredName.size() - 4, 4, "_sh.nif");
|
|
|
|
bool isEnchanted = !shield->getClass().getEnchantment(*shield).empty();
|
|
|
|
|
2022-08-21 18:53:38 +02:00
|
|
|
// If we have no dedicated sheath model, use basic shield model as fallback.
|
2019-08-11 15:01:48 +04:00
|
|
|
if (!mResourceSystem->getVFS()->exists(holsteredName))
|
|
|
|
mHolsteredShield = attachMesh(mesh, boneName, isEnchanted, &glowColor);
|
|
|
|
else
|
|
|
|
mHolsteredShield = attachMesh(holsteredName, boneName, isEnchanted, &glowColor);
|
|
|
|
|
|
|
|
if (!mHolsteredShield)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor("Bip01 Sheath");
|
|
|
|
mHolsteredShield->getNode()->accept(findVisitor);
|
|
|
|
osg::Group* shieldNode = findVisitor.mFoundNode;
|
|
|
|
|
|
|
|
// If mesh author declared an empty sheath node, use transformation from this node, but use the common shield
|
|
|
|
// mesh. This approach allows to tweak shield position without need to store the whole shield mesh in the _sh
|
|
|
|
// file.
|
|
|
|
if (shieldNode && !shieldNode->getNumChildren())
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-08-11 15:01:48 +04:00
|
|
|
osg::ref_ptr<osg::Node> fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, shieldNode);
|
|
|
|
if (isEnchanted)
|
|
|
|
SceneUtil::addEnchantedGlow(shieldNode, mResourceSystem, glowColor);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
|
|
|
}
|
2019-08-11 15:01:48 +04:00
|
|
|
|
|
|
|
bool ActorAnimation::useShieldAnimations() const
|
|
|
|
{
|
2023-06-27 23:41:06 +02:00
|
|
|
if (!Settings::game().mShieldSheathing)
|
2019-08-11 15:01:48 +04:00
|
|
|
return false;
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
const MWWorld::Class& cls = mPtr.getClass();
|
|
|
|
if (!cls.hasInventoryStore(mPtr))
|
|
|
|
return false;
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
if (getTextKeyTime("shield: equip attach") < 0 || getTextKeyTime("shield: unequip detach") < 0)
|
|
|
|
return false;
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
|
|
|
|
const MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
|
|
const MWWorld::ConstContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
|
|
if (weapon != inv.end() && shield != inv.end() && shield->getType() == ESM::Armor::sRecordId
|
|
|
|
&& !getSheathedShieldMesh(*shield).empty())
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-08-11 15:01:48 +04:00
|
|
|
auto type = weapon->getType();
|
2021-10-11 11:46:21 +00:00
|
|
|
if (type == ESM::Weapon::sRecordId)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-08-11 15:01:48 +04:00
|
|
|
const MWWorld::LiveCellRef<ESM::Weapon>* ref = weapon->get<ESM::Weapon>();
|
|
|
|
ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;
|
|
|
|
return !(MWMechanics::getWeaponType(weaponType)->mFlags & ESM::WeaponType::TwoHanded);
|
|
|
|
}
|
2021-10-11 11:46:21 +00:00
|
|
|
else if (type == ESM::Lockpick::sRecordId || type == ESM::Probe::sRecordId)
|
2019-08-11 15:01:48 +04:00
|
|
|
return true;
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2019-08-11 15:01:48 +04:00
|
|
|
|
|
|
|
return false;
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2019-08-11 15:01:48 +04:00
|
|
|
|
|
|
|
osg::Group* ActorAnimation::getBoneByName(std::string_view boneName) const
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-08-11 15:01:48 +04:00
|
|
|
if (!mObjectRoot)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor(boneName);
|
|
|
|
mObjectRoot->accept(findVisitor);
|
|
|
|
|
|
|
|
return findVisitor.mFoundNode;
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
std::string_view ActorAnimation::getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon)
|
|
|
|
{
|
2018-03-03 14:16:21 +04:00
|
|
|
if (weapon.isEmpty())
|
2022-08-21 18:53:38 +02:00
|
|
|
return {};
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2021-10-11 11:46:21 +00:00
|
|
|
auto type = weapon.getClass().getType();
|
|
|
|
if (type == ESM::Weapon::sRecordId)
|
2019-08-11 15:01:48 +04:00
|
|
|
{
|
|
|
|
const MWWorld::LiveCellRef<ESM::Weapon>* ref = weapon.get<ESM::Weapon>();
|
|
|
|
int weaponType = ref->mBase->mData.mType;
|
|
|
|
return MWMechanics::getWeaponType(weaponType)->mSheathingBone;
|
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActorAnimation::resetControllers(osg::Node* node)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-03-04 22:43:44 +04:00
|
|
|
if (node == nullptr)
|
2019-08-11 15:01:48 +04:00
|
|
|
return;
|
|
|
|
|
2023-12-13 20:59:31 +03:00
|
|
|
// This is used to avoid playing animations intended for equipped weapons on holstered weapons.
|
2022-08-21 18:53:38 +02:00
|
|
|
SceneUtil::ForceControllerSourcesVisitor removeVisitor(std::make_shared<NullAnimationTime>());
|
2018-03-03 14:16:21 +04:00
|
|
|
node->accept(removeVisitor);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-08-21 18:53:38 +02:00
|
|
|
void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2023-06-27 23:41:06 +02:00
|
|
|
if (!Settings::game().mWeaponSheathing)
|
2021-09-12 07:56:20 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!mPtr.getClass().hasInventoryStore(mPtr))
|
|
|
|
return;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-08-21 18:53:38 +02:00
|
|
|
mScabbard.reset();
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2021-10-11 11:46:21 +00:00
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
2018-03-03 14:16:21 +04:00
|
|
|
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
2018-12-26 13:45:28 +04:00
|
|
|
if (weapon == inv.end() || weapon->getType() != ESM::Weapon::sRecordId)
|
|
|
|
return;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-08-21 18:53:38 +02:00
|
|
|
// Since throwing weapons stack themselves, do not show such weapon itself
|
|
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
2018-12-26 13:45:28 +04:00
|
|
|
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
|
2022-08-21 18:53:38 +02:00
|
|
|
showHolsteredWeapons = false;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2019-03-04 22:43:44 +04:00
|
|
|
std::string mesh = weapon->getClass().getModel(*weapon);
|
|
|
|
std::string scabbardName = mesh;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-04-03 23:23:25 +02:00
|
|
|
std::string_view boneName = getHolsteredWeaponBoneName(*weapon);
|
|
|
|
if (mesh.empty() || boneName.empty())
|
|
|
|
return;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2023-12-13 20:59:31 +03:00
|
|
|
// If the scabbard is not found, use the weapon mesh as fallback.
|
2018-03-03 14:16:21 +04:00
|
|
|
scabbardName = scabbardName.replace(scabbardName.size() - 4, 4, "_sh.nif");
|
|
|
|
bool isEnchanted = !weapon->getClass().getEnchantment(*weapon).empty();
|
|
|
|
if (!mResourceSystem->getVFS()->exists(scabbardName))
|
|
|
|
{
|
|
|
|
if (showHolsteredWeapons)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-08-07 08:51:46 +04:00
|
|
|
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
|
2019-08-27 09:55:02 +04:00
|
|
|
mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor);
|
2019-03-10 11:03:51 +04:00
|
|
|
if (mScabbard)
|
|
|
|
resetControllers(mScabbard->getNode());
|
2018-03-03 14:16:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-27 09:55:02 +04:00
|
|
|
mScabbard = attachMesh(scabbardName, boneName);
|
2021-04-20 16:01:01 +04:00
|
|
|
if (mScabbard)
|
|
|
|
resetControllers(mScabbard->getNode());
|
2018-03-03 14:16:21 +04:00
|
|
|
|
|
|
|
osg::Group* weaponNode = getBoneByName("Bip01 Weapon");
|
|
|
|
if (!weaponNode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// When we draw weapon, hide the Weapon node from sheath model.
|
|
|
|
// Otherwise add the enchanted glow to it.
|
|
|
|
if (!showHolsteredWeapons)
|
|
|
|
{
|
|
|
|
weaponNode->setNodeMask(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If mesh author declared empty weapon node, use transformation from this node, but use the common weapon
|
|
|
|
// mesh. This approach allows to tweak weapon position without need to store the whole weapon mesh in the
|
|
|
|
// _sh file.
|
|
|
|
if (!weaponNode->getNumChildren())
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2018-03-03 14:16:21 +04:00
|
|
|
osg::ref_ptr<osg::Node> fallbackNode
|
2019-02-17 11:42:22 +04:00
|
|
|
= mResourceSystem->getSceneManager()->getInstance(mesh, weaponNode);
|
2019-03-04 22:43:44 +04:00
|
|
|
resetControllers(fallbackNode);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
|
|
|
|
2018-03-03 14:16:21 +04:00
|
|
|
if (isEnchanted)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-08-07 08:51:46 +04:00
|
|
|
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
|
2019-08-07 11:03:26 +04:00
|
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(weaponNode, mResourceSystem, glowColor);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActorAnimation::updateQuiver()
|
|
|
|
{
|
2023-06-27 23:41:06 +02:00
|
|
|
if (!Settings::game().mWeaponSheathing)
|
2018-03-03 14:16:21 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!mPtr.getClass().hasInventoryStore(mPtr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
|
|
if (weapon == inv.end() || weapon->getType() != ESM::Weapon::sRecordId)
|
|
|
|
return;
|
|
|
|
|
2018-12-26 13:45:28 +04:00
|
|
|
std::string mesh = weapon->getClass().getModel(*weapon);
|
|
|
|
std::string_view boneName = getHolsteredWeaponBoneName(*weapon);
|
|
|
|
if (mesh.empty() || boneName.empty())
|
2022-09-22 21:26:05 +03:00
|
|
|
return;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
|
|
|
osg::Group* ammoNode = getBoneByName("Bip01 Ammo");
|
2019-02-17 11:42:22 +04:00
|
|
|
if (!ammoNode)
|
|
|
|
return;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2019-02-17 11:42:22 +04:00
|
|
|
// Special case for throwing weapons - they do not use ammo, but they stack themselves
|
|
|
|
bool suitableAmmo = false;
|
|
|
|
MWWorld::ConstContainerStoreIterator ammo = weapon;
|
|
|
|
unsigned int ammoCount = 0;
|
2018-12-26 13:45:28 +04:00
|
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
2019-02-17 11:42:22 +04:00
|
|
|
const auto& weaponType = MWMechanics::getWeaponType(type);
|
|
|
|
if (weaponType->mWeaponClass == ESM::WeaponType::Thrown)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2023-12-31 17:12:46 +00:00
|
|
|
ammoCount = ammo->getCellRef().getCount();
|
2018-12-26 13:45:28 +04:00
|
|
|
osg::Group* throwingWeaponNode = getBoneByName(weaponType->mAttachBone);
|
2019-02-17 11:42:22 +04:00
|
|
|
if (throwingWeaponNode && throwingWeaponNode->getNumChildren())
|
2018-03-03 14:16:21 +04:00
|
|
|
ammoCount--;
|
|
|
|
|
2019-02-17 11:42:22 +04:00
|
|
|
suitableAmmo = true;
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2019-02-17 11:42:22 +04:00
|
|
|
else
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-02-17 11:42:22 +04:00
|
|
|
ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
|
|
|
if (ammo == inv.end())
|
|
|
|
return;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2023-12-31 17:12:46 +00:00
|
|
|
ammoCount = ammo->getCellRef().getCount();
|
2019-02-17 11:42:22 +04:00
|
|
|
bool arrowAttached = isArrowAttached();
|
|
|
|
if (arrowAttached)
|
|
|
|
ammoCount--;
|
|
|
|
|
2018-12-26 13:45:28 +04:00
|
|
|
suitableAmmo = ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponType->mAmmoType;
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
|
|
|
|
2018-03-03 14:16:21 +04:00
|
|
|
if (!suitableAmmo)
|
2022-09-22 21:26:05 +03:00
|
|
|
return;
|
|
|
|
|
2018-03-03 14:16:21 +04:00
|
|
|
// We should not show more ammo than equipped and more than quiver mesh has
|
|
|
|
ammoCount = std::min(ammoCount, ammoNode->getNumChildren());
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2019-02-17 11:42:22 +04:00
|
|
|
// Remove existing ammo nodes
|
|
|
|
for (unsigned int i = 0; i < ammoNode->getNumChildren(); ++i)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2019-02-17 11:42:22 +04:00
|
|
|
osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();
|
2018-03-03 14:16:21 +04:00
|
|
|
if (!arrowNode->getNumChildren())
|
2019-02-17 11:42:22 +04:00
|
|
|
continue;
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2019-02-17 11:42:22 +04:00
|
|
|
osg::ref_ptr<osg::Node> arrowChildNode = arrowNode->getChild(0);
|
|
|
|
arrowNode->removeChild(arrowChildNode);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
|
|
|
|
2019-02-17 11:42:22 +04:00
|
|
|
// Add new ones
|
2019-08-07 08:51:46 +04:00
|
|
|
osg::Vec4f glowColor = ammo->getClass().getEnchantmentColor(*ammo);
|
2019-02-17 11:42:22 +04:00
|
|
|
std::string model = ammo->getClass().getModel(*ammo);
|
|
|
|
for (unsigned int i = 0; i < ammoCount; ++i)
|
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();
|
|
|
|
osg::ref_ptr<osg::Node> arrow = mResourceSystem->getSceneManager()->getInstance(model, arrowNode);
|
2019-08-09 21:38:03 +04:00
|
|
|
if (!ammo->getClass().getEnchantment(*ammo).empty())
|
2023-07-29 12:58:27 +04:00
|
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(std::move(arrow), mResourceSystem, glowColor);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
}
|
2016-10-08 03:49:50 +02:00
|
|
|
|
|
|
|
void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/)
|
|
|
|
{
|
|
|
|
if (item.getType() == ESM::Light::sRecordId)
|
|
|
|
{
|
|
|
|
const ESM::Light* light = item.get<ESM::Light>()->mBase;
|
|
|
|
if (!(light->mData.mFlags & ESM::Light::Carry))
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2016-10-08 03:49:50 +02:00
|
|
|
addHiddenItemLight(item, light);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2016-10-08 03:49:50 +02:00
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
|
|
|
|
if (!mPtr.getClass().hasInventoryStore(mPtr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If the count of equipped ammo or throwing weapon was changed, we should update quiver
|
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
2021-10-11 11:46:21 +00:00
|
|
|
if (weapon == inv.end() || weapon->getType() != ESM::Weapon::sRecordId)
|
2018-03-03 14:16:21 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
MWWorld::ConstContainerStoreIterator ammo = inv.end();
|
2018-12-26 13:45:28 +04:00
|
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
|
|
|
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
|
2018-03-03 14:16:21 +04:00
|
|
|
ammo = weapon;
|
|
|
|
else
|
|
|
|
ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
|
|
|
|
|
|
|
if (ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId())
|
|
|
|
updateQuiver();
|
2016-10-08 03:49:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ActorAnimation::itemRemoved(const MWWorld::ConstPtr& item, int /*count*/)
|
|
|
|
{
|
|
|
|
if (item.getType() == ESM::Light::sRecordId)
|
|
|
|
{
|
|
|
|
ItemLightMap::iterator iter = mItemLights.find(item);
|
|
|
|
if (iter != mItemLights.end())
|
|
|
|
{
|
2023-12-31 17:12:46 +00:00
|
|
|
if (!item.getCellRef().getCount())
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2016-10-08 03:49:50 +02:00
|
|
|
removeHiddenItemLight(item);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2016-10-08 03:49:50 +02:00
|
|
|
}
|
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
|
|
|
|
if (!mPtr.getClass().hasInventoryStore(mPtr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If the count of equipped ammo or throwing weapon was changed, we should update quiver
|
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
2021-10-11 11:46:21 +00:00
|
|
|
if (weapon == inv.end() || weapon->getType() != ESM::Weapon::sRecordId)
|
2022-09-22 21:26:05 +03:00
|
|
|
return;
|
2018-03-03 14:16:21 +04:00
|
|
|
|
|
|
|
MWWorld::ConstContainerStoreIterator ammo = inv.end();
|
|
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
|
|
|
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
|
|
|
|
ammo = weapon;
|
|
|
|
else
|
|
|
|
ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
2016-10-08 03:49:50 +02:00
|
|
|
|
|
|
|
if (ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId())
|
|
|
|
updateQuiver();
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2016-10-08 03:49:50 +02:00
|
|
|
|
|
|
|
void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2016-10-08 03:49:50 +02:00
|
|
|
if (mItemLights.find(item) != mItemLights.end())
|
|
|
|
return;
|
|
|
|
|
2020-04-20 18:47:14 +02:00
|
|
|
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
|
2016-10-08 03:49:50 +02:00
|
|
|
|
|
|
|
osg::Vec4f ambient(1, 1, 1, 1);
|
2020-04-20 18:47:14 +02:00
|
|
|
osg::ref_ptr<SceneUtil::LightSource> lightSource
|
2023-02-07 11:30:57 +01:00
|
|
|
= SceneUtil::createLightSource(SceneUtil::LightCommon(*esmLight), Mask_Lighting, exterior, ambient);
|
2016-10-08 03:49:50 +02:00
|
|
|
|
2016-10-11 22:15:51 +02:00
|
|
|
mInsert->addChild(lightSource);
|
2016-10-08 03:49:50 +02:00
|
|
|
|
|
|
|
if (mLightListCallback && mPtr == MWMechanics::getPlayer())
|
|
|
|
mLightListCallback->getIgnoredLightSources().insert(lightSource.get());
|
|
|
|
|
|
|
|
mItemLights.insert(std::make_pair(item, lightSource));
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
2016-10-08 03:49:50 +02:00
|
|
|
|
2016-10-11 22:15:51 +02:00
|
|
|
void ActorAnimation::removeHiddenItemLight(const MWWorld::ConstPtr& item)
|
2016-10-08 03:49:50 +02:00
|
|
|
{
|
2016-10-11 22:15:51 +02:00
|
|
|
ItemLightMap::iterator iter = mItemLights.find(item);
|
|
|
|
if (iter == mItemLights.end())
|
|
|
|
return;
|
2016-10-08 03:49:50 +02:00
|
|
|
|
|
|
|
if (mLightListCallback && mPtr == MWMechanics::getPlayer())
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
2016-10-08 03:49:50 +02:00
|
|
|
std::set<SceneUtil::LightSource*>::iterator ignoredIter
|
|
|
|
= mLightListCallback->getIgnoredLightSources().find(iter->second.get());
|
|
|
|
if (ignoredIter != mLightListCallback->getIgnoredLightSources().end())
|
|
|
|
mLightListCallback->getIgnoredLightSources().erase(ignoredIter);
|
|
|
|
}
|
|
|
|
|
2021-11-02 18:01:22 +01:00
|
|
|
mInsert->removeChild(iter->second);
|
2016-10-08 03:49:50 +02:00
|
|
|
mItemLights.erase(iter);
|
2022-09-22 21:26:05 +03:00
|
|
|
}
|
|
|
|
|
2022-07-23 18:30:28 +02:00
|
|
|
void ActorAnimation::removeFromScene()
|
|
|
|
{
|
2022-10-02 13:50:41 +02:00
|
|
|
removeFromSceneImpl();
|
2022-07-23 18:30:28 +02:00
|
|
|
Animation::removeFromScene();
|
|
|
|
}
|
|
|
|
|
2022-10-02 13:50:41 +02:00
|
|
|
void ActorAnimation::removeFromSceneImpl()
|
|
|
|
{
|
|
|
|
for (const auto& [k, v] : mItemLights)
|
|
|
|
mInsert->removeChild(v);
|
|
|
|
}
|
2016-10-08 03:49:50 +02:00
|
|
|
}
|