mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-31 15:32:45 +00:00
125b21de20
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!
600 lines
23 KiB
C++
600 lines
23 KiB
C++
#include "actoranimation.hpp"
|
|
#include <utility>
|
|
|
|
#include <osg/Group>
|
|
#include <osg/Node>
|
|
#include <osg/Vec4f>
|
|
|
|
#include <components/esm3/loadbody.hpp>
|
|
#include <components/esm3/loadcell.hpp>
|
|
#include <components/esm3/loadligh.hpp>
|
|
|
|
#include <components/resource/resourcesystem.hpp>
|
|
#include <components/resource/scenemanager.hpp>
|
|
|
|
#include <components/sceneutil/attach.hpp>
|
|
#include <components/sceneutil/lightmanager.hpp>
|
|
#include <components/sceneutil/lightutil.hpp>
|
|
#include <components/sceneutil/visitor.hpp>
|
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
|
|
|
#include <components/settings/settings.hpp>
|
|
|
|
#include <components/vfs/manager.hpp>
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/world.hpp"
|
|
#include "../mwmechanics/actorutil.hpp"
|
|
#include "../mwmechanics/creaturestats.hpp"
|
|
#include "../mwmechanics/drawstate.hpp"
|
|
#include "../mwmechanics/weapontype.hpp"
|
|
#include "../mwworld/cellstore.hpp"
|
|
#include "../mwworld/class.hpp"
|
|
#include "../mwworld/esmstore.hpp"
|
|
#include "../mwworld/inventorystore.hpp"
|
|
#include "../mwworld/ptr.hpp"
|
|
|
|
#include "vismask.hpp"
|
|
|
|
namespace MWRender
|
|
{
|
|
|
|
ActorAnimation::ActorAnimation(
|
|
const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem)
|
|
: Animation(ptr, parentNode, resourceSystem)
|
|
{
|
|
MWWorld::ContainerStore& store = mPtr.getClass().getContainerStore(mPtr);
|
|
|
|
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))
|
|
{
|
|
addHiddenItemLight(*iter, light);
|
|
}
|
|
}
|
|
|
|
// Make sure we cleaned object from effects, just in cast if we re-use node
|
|
removeEffects();
|
|
}
|
|
|
|
ActorAnimation::~ActorAnimation()
|
|
{
|
|
removeFromSceneImpl();
|
|
}
|
|
|
|
PartHolderPtr ActorAnimation::attachMesh(
|
|
const std::string& model, std::string_view bonename, bool enchantedGlow, osg::Vec4f* glowColor)
|
|
{
|
|
osg::Group* parent = getBoneByName(bonename);
|
|
if (!parent)
|
|
return nullptr;
|
|
|
|
osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->getInstance(model, parent);
|
|
|
|
const NodeMap& nodeMap = getNodeMap();
|
|
NodeMap::const_iterator found = nodeMap.find(bonename);
|
|
if (found == nodeMap.end())
|
|
return {};
|
|
|
|
if (enchantedGlow)
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(instance, mResourceSystem, *glowColor);
|
|
|
|
return std::make_unique<PartHolder>(instance);
|
|
}
|
|
|
|
osg::ref_ptr<osg::Node> ActorAnimation::attach(
|
|
const std::string& model, std::string_view bonename, std::string_view bonefilter, bool isLight)
|
|
{
|
|
osg::ref_ptr<const osg::Node> templateNode = mResourceSystem->getSceneManager()->getTemplate(model);
|
|
|
|
const NodeMap& nodeMap = getNodeMap();
|
|
auto found = nodeMap.find(bonename);
|
|
if (found == nodeMap.end())
|
|
throw std::runtime_error("Can't find attachment node " + std::string{ bonename });
|
|
if (isLight)
|
|
{
|
|
osg::Quat rotation(osg::DegreesToRadians(-90.f), osg::Vec3f(1, 0, 0));
|
|
return SceneUtil::attach(
|
|
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager(), &rotation);
|
|
}
|
|
return SceneUtil::attach(
|
|
templateNode, mObjectRoot, bonefilter, found->second, mResourceSystem->getSceneManager());
|
|
}
|
|
|
|
std::string ActorAnimation::getShieldMesh(const MWWorld::ConstPtr& shield, bool female) const
|
|
{
|
|
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())
|
|
{
|
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
|
const MWWorld::Store<ESM::BodyPart>& partStore = store.get<ESM::BodyPart>();
|
|
for (const auto& part : bodyparts)
|
|
{
|
|
if (part.mPart != ESM::PRT_Shield)
|
|
continue;
|
|
|
|
const ESM::RefId* bodypartName = nullptr;
|
|
if (female && !part.mFemale.empty())
|
|
bodypartName = &part.mFemale;
|
|
else if (!part.mMale.empty())
|
|
bodypartName = &part.mMale;
|
|
|
|
if (bodypartName && !bodypartName->empty())
|
|
{
|
|
const ESM::BodyPart* bodypart = partStore.search(*bodypartName);
|
|
if (bodypart == nullptr || bodypart->mData.mType != ESM::BodyPart::MT_Armor)
|
|
return std::string();
|
|
if (!bodypart->mModel.empty())
|
|
return Misc::ResourceHelpers::correctMeshPath(
|
|
bodypart->mModel, MWBase::Environment::get().getResourceSystem()->getVFS());
|
|
}
|
|
}
|
|
}
|
|
return shield.getClass().getModel(shield);
|
|
}
|
|
|
|
std::string ActorAnimation::getSheathedShieldMesh(const MWWorld::ConstPtr& shield) const
|
|
{
|
|
std::string mesh = getShieldMesh(shield, false);
|
|
|
|
if (mesh.empty())
|
|
return mesh;
|
|
|
|
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
|
|
{
|
|
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
|
|
if (shieldSheathing)
|
|
{
|
|
const MWWorld::Class& cls = mPtr.getClass();
|
|
MWMechanics::CreatureStats& stats = cls.getCreatureStats(mPtr);
|
|
if (cls.hasInventoryStore(mPtr) && stats.getDrawState() == MWMechanics::DrawState::Nothing)
|
|
{
|
|
SceneUtil::FindByNameVisitor findVisitor("Bip01 AttachShield");
|
|
mObjectRoot->accept(findVisitor);
|
|
if (findVisitor.mFoundNode)
|
|
{
|
|
const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
|
|
const MWWorld::ConstContainerStoreIterator shield
|
|
= inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
if (shield != inv.end() && shield->getType() == ESM::Armor::sRecordId
|
|
&& !getSheathedShieldMesh(*shield).empty())
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return !(MWMechanics::getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);
|
|
}
|
|
|
|
void ActorAnimation::updateHolsteredShield(bool showCarriedLeft)
|
|
{
|
|
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
|
|
if (!shieldSheathing)
|
|
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);
|
|
if (weapon == inv.end())
|
|
return;
|
|
|
|
auto type = weapon->getType();
|
|
if (type == ESM::Weapon::sRecordId)
|
|
{
|
|
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;
|
|
}
|
|
|
|
std::string mesh = getSheathedShieldMesh(*shield);
|
|
if (mesh.empty())
|
|
return;
|
|
|
|
std::string_view boneName = "Bip01 AttachShield";
|
|
osg::Vec4f glowColor = shield->getClass().getEnchantmentColor(*shield);
|
|
std::string holsteredName = mesh;
|
|
holsteredName = holsteredName.replace(holsteredName.size() - 4, 4, "_sh.nif");
|
|
bool isEnchanted = !shield->getClass().getEnchantment(*shield).empty();
|
|
|
|
// If we have no dedicated sheath model, use basic shield model as fallback.
|
|
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())
|
|
{
|
|
osg::ref_ptr<osg::Node> fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, shieldNode);
|
|
if (isEnchanted)
|
|
SceneUtil::addEnchantedGlow(shieldNode, mResourceSystem, glowColor);
|
|
}
|
|
}
|
|
|
|
bool ActorAnimation::useShieldAnimations() const
|
|
{
|
|
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
|
|
if (!shieldSheathing)
|
|
return false;
|
|
|
|
const MWWorld::Class& cls = mPtr.getClass();
|
|
if (!cls.hasInventoryStore(mPtr))
|
|
return false;
|
|
|
|
if (getTextKeyTime("shield: equip attach") < 0 || getTextKeyTime("shield: unequip detach") < 0)
|
|
return false;
|
|
|
|
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())
|
|
{
|
|
auto type = weapon->getType();
|
|
if (type == ESM::Weapon::sRecordId)
|
|
{
|
|
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);
|
|
}
|
|
else if (type == ESM::Lockpick::sRecordId || type == ESM::Probe::sRecordId)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
osg::Group* ActorAnimation::getBoneByName(std::string_view boneName) const
|
|
{
|
|
if (!mObjectRoot)
|
|
return nullptr;
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor(boneName);
|
|
mObjectRoot->accept(findVisitor);
|
|
|
|
return findVisitor.mFoundNode;
|
|
}
|
|
|
|
std::string_view ActorAnimation::getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon)
|
|
{
|
|
if (weapon.isEmpty())
|
|
return {};
|
|
|
|
auto type = weapon.getClass().getType();
|
|
if (type == ESM::Weapon::sRecordId)
|
|
{
|
|
const MWWorld::LiveCellRef<ESM::Weapon>* ref = weapon.get<ESM::Weapon>();
|
|
int weaponType = ref->mBase->mData.mType;
|
|
return MWMechanics::getWeaponType(weaponType)->mSheathingBone;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
void ActorAnimation::resetControllers(osg::Node* node)
|
|
{
|
|
if (node == nullptr)
|
|
return;
|
|
|
|
SceneUtil::ForceControllerSourcesVisitor removeVisitor(std::make_shared<NullAnimationTime>());
|
|
node->accept(removeVisitor);
|
|
}
|
|
|
|
void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
|
|
{
|
|
static const bool weaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game");
|
|
if (!weaponSheathing)
|
|
return;
|
|
|
|
if (!mPtr.getClass().hasInventoryStore(mPtr))
|
|
return;
|
|
|
|
mScabbard.reset();
|
|
|
|
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;
|
|
|
|
// Since throwing weapons stack themselves, do not show such weapon itself
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
|
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
|
|
showHolsteredWeapons = false;
|
|
|
|
std::string mesh = weapon->getClass().getModel(*weapon);
|
|
std::string scabbardName = mesh;
|
|
|
|
std::string_view boneName = getHolsteredWeaponBoneName(*weapon);
|
|
if (mesh.empty() || boneName.empty())
|
|
return;
|
|
|
|
// If the scabbard is not found, use a weapon mesh as fallback.
|
|
// Note: it is unclear how to handle time for controllers attached to bodyparts, so disable them for now.
|
|
// We use the similar approach for other bodyparts.
|
|
scabbardName = scabbardName.replace(scabbardName.size() - 4, 4, "_sh.nif");
|
|
bool isEnchanted = !weapon->getClass().getEnchantment(*weapon).empty();
|
|
if (!mResourceSystem->getVFS()->exists(scabbardName))
|
|
{
|
|
if (showHolsteredWeapons)
|
|
{
|
|
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
|
|
mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor);
|
|
if (mScabbard)
|
|
resetControllers(mScabbard->getNode());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
mScabbard = attachMesh(scabbardName, boneName);
|
|
if (mScabbard)
|
|
resetControllers(mScabbard->getNode());
|
|
|
|
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())
|
|
{
|
|
osg::ref_ptr<osg::Node> fallbackNode
|
|
= mResourceSystem->getSceneManager()->getInstance(mesh, weaponNode);
|
|
resetControllers(fallbackNode);
|
|
}
|
|
|
|
if (isEnchanted)
|
|
{
|
|
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(weaponNode, mResourceSystem, glowColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ActorAnimation::updateQuiver()
|
|
{
|
|
static const bool weaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game");
|
|
if (!weaponSheathing)
|
|
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;
|
|
|
|
std::string mesh = weapon->getClass().getModel(*weapon);
|
|
std::string_view boneName = getHolsteredWeaponBoneName(*weapon);
|
|
if (mesh.empty() || boneName.empty())
|
|
return;
|
|
|
|
osg::Group* ammoNode = getBoneByName("Bip01 Ammo");
|
|
if (!ammoNode)
|
|
return;
|
|
|
|
// 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;
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
|
const auto& weaponType = MWMechanics::getWeaponType(type);
|
|
if (weaponType->mWeaponClass == ESM::WeaponType::Thrown)
|
|
{
|
|
ammoCount = ammo->getRefData().getCount();
|
|
osg::Group* throwingWeaponNode = getBoneByName(weaponType->mAttachBone);
|
|
if (throwingWeaponNode && throwingWeaponNode->getNumChildren())
|
|
ammoCount--;
|
|
|
|
suitableAmmo = true;
|
|
}
|
|
else
|
|
{
|
|
ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
|
if (ammo == inv.end())
|
|
return;
|
|
|
|
ammoCount = ammo->getRefData().getCount();
|
|
bool arrowAttached = isArrowAttached();
|
|
if (arrowAttached)
|
|
ammoCount--;
|
|
|
|
suitableAmmo = ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponType->mAmmoType;
|
|
}
|
|
|
|
if (!suitableAmmo)
|
|
return;
|
|
|
|
// We should not show more ammo than equipped and more than quiver mesh has
|
|
ammoCount = std::min(ammoCount, ammoNode->getNumChildren());
|
|
|
|
// Remove existing ammo nodes
|
|
for (unsigned int i = 0; i < ammoNode->getNumChildren(); ++i)
|
|
{
|
|
osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();
|
|
if (!arrowNode->getNumChildren())
|
|
continue;
|
|
|
|
osg::ref_ptr<osg::Node> arrowChildNode = arrowNode->getChild(0);
|
|
arrowNode->removeChild(arrowChildNode);
|
|
}
|
|
|
|
// Add new ones
|
|
osg::Vec4f glowColor = ammo->getClass().getEnchantmentColor(*ammo);
|
|
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);
|
|
if (!ammo->getClass().getEnchantment(*ammo).empty())
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(arrow, mResourceSystem, glowColor);
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
addHiddenItemLight(item, light);
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (weapon == inv.end() || weapon->getType() != ESM::Weapon::sRecordId)
|
|
return;
|
|
|
|
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);
|
|
|
|
if (ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId())
|
|
updateQuiver();
|
|
}
|
|
|
|
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())
|
|
{
|
|
if (!item.getRefData().getCount())
|
|
{
|
|
removeHiddenItemLight(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (weapon == inv.end() || weapon->getType() != ESM::Weapon::sRecordId)
|
|
return;
|
|
|
|
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);
|
|
|
|
if (ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId())
|
|
updateQuiver();
|
|
}
|
|
|
|
void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight)
|
|
{
|
|
if (mItemLights.find(item) != mItemLights.end())
|
|
return;
|
|
|
|
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
|
|
|
|
osg::Vec4f ambient(1, 1, 1, 1);
|
|
osg::ref_ptr<SceneUtil::LightSource> lightSource
|
|
= SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, ambient);
|
|
|
|
mInsert->addChild(lightSource);
|
|
|
|
if (mLightListCallback && mPtr == MWMechanics::getPlayer())
|
|
mLightListCallback->getIgnoredLightSources().insert(lightSource.get());
|
|
|
|
mItemLights.insert(std::make_pair(item, lightSource));
|
|
}
|
|
|
|
void ActorAnimation::removeHiddenItemLight(const MWWorld::ConstPtr& item)
|
|
{
|
|
ItemLightMap::iterator iter = mItemLights.find(item);
|
|
if (iter == mItemLights.end())
|
|
return;
|
|
|
|
if (mLightListCallback && mPtr == MWMechanics::getPlayer())
|
|
{
|
|
std::set<SceneUtil::LightSource*>::iterator ignoredIter
|
|
= mLightListCallback->getIgnoredLightSources().find(iter->second.get());
|
|
if (ignoredIter != mLightListCallback->getIgnoredLightSources().end())
|
|
mLightListCallback->getIgnoredLightSources().erase(ignoredIter);
|
|
}
|
|
|
|
mInsert->removeChild(iter->second);
|
|
mItemLights.erase(iter);
|
|
}
|
|
|
|
void ActorAnimation::removeFromScene()
|
|
{
|
|
removeFromSceneImpl();
|
|
Animation::removeFromScene();
|
|
}
|
|
|
|
void ActorAnimation::removeFromSceneImpl()
|
|
{
|
|
for (const auto& [k, v] : mItemLights)
|
|
mInsert->removeChild(v);
|
|
}
|
|
}
|