mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-10 12:39:53 +00:00
Shields holstering support (feature #5193)
This commit is contained in:
parent
e8037473ca
commit
d3a3b2f1f6
@ -209,6 +209,7 @@
|
|||||||
Feature #5132: Unique animations for different weapon types
|
Feature #5132: Unique animations for different weapon types
|
||||||
Feature #5146: Safe Dispose corpse
|
Feature #5146: Safe Dispose corpse
|
||||||
Feature #5147: Show spell magicka cost in spell buying window
|
Feature #5147: Show spell magicka cost in spell buying window
|
||||||
|
Feature #5193: Weapon sheathing
|
||||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||||
Task #4695: Optimize Distant Terrain memory consumption
|
Task #4695: Optimize Distant Terrain memory consumption
|
||||||
Task #4789: Optimize cell transitions
|
Task #4789: Optimize cell transitions
|
||||||
|
@ -985,7 +985,11 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
|
|||||||
size_t off = groupname.size()+2;
|
size_t off = groupname.size()+2;
|
||||||
size_t len = evt.size() - off;
|
size_t len = evt.size() - off;
|
||||||
|
|
||||||
if(evt.compare(off, len, "equip attach") == 0)
|
if(groupname == "shield" && evt.compare(off, len, "equip attach") == 0)
|
||||||
|
mAnimation->showCarriedLeft(true);
|
||||||
|
else if(groupname == "shield" && evt.compare(off, len, "unequip detach") == 0)
|
||||||
|
mAnimation->showCarriedLeft(false);
|
||||||
|
else if(evt.compare(off, len, "equip attach") == 0)
|
||||||
mAnimation->showWeapons(true);
|
mAnimation->showWeapons(true);
|
||||||
else if(evt.compare(off, len, "unequip detach") == 0)
|
else if(evt.compare(off, len, "unequip detach") == 0)
|
||||||
mAnimation->showWeapons(false);
|
mAnimation->showWeapons(false);
|
||||||
@ -1193,7 +1197,7 @@ bool CharacterController::updateCarriedLeftVisible(const int weaptype) const
|
|||||||
// Shields/torches shouldn't be visible during any operation involving two hands
|
// Shields/torches shouldn't be visible during any operation involving two hands
|
||||||
// There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop",
|
// There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop",
|
||||||
// but they are also present in weapon drawing animation.
|
// but they are also present in weapon drawing animation.
|
||||||
return !(getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);
|
return mAnimation->updateCarriedLeftVisible(weaptype);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::updateWeaponState(CharacterState& idle)
|
bool CharacterController::updateWeaponState(CharacterState& idle)
|
||||||
@ -1275,8 +1279,19 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
|||||||
{
|
{
|
||||||
// Note: we do not disable unequipping animation automatically to avoid body desync
|
// Note: we do not disable unequipping animation automatically to avoid body desync
|
||||||
weapgroup = getWeaponAnimation(mWeaponType);
|
weapgroup = getWeaponAnimation(mWeaponType);
|
||||||
mAnimation->play(weapgroup, priorityWeapon,
|
int unequipMask = MWRender::Animation::BlendMask_All;
|
||||||
MWRender::Animation::BlendMask_All, false,
|
bool useShieldAnims = mAnimation->useShieldAnimations();
|
||||||
|
if (useShieldAnims && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && !(mWeaponType == ESM::Weapon::None && weaptype == ESM::Weapon::Spell))
|
||||||
|
{
|
||||||
|
unequipMask = unequipMask |~MWRender::Animation::BlendMask_LeftArm;
|
||||||
|
mAnimation->play("shield", Priority_Block,
|
||||||
|
MWRender::Animation::BlendMask_LeftArm, true,
|
||||||
|
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||||
|
}
|
||||||
|
else if (mWeaponType == ESM::Weapon::HandToHand)
|
||||||
|
mAnimation->showCarriedLeft(false);
|
||||||
|
|
||||||
|
mAnimation->play(weapgroup, priorityWeapon, unequipMask, false,
|
||||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||||
|
|
||||||
@ -1301,7 +1316,10 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
|||||||
if (weaptype != mWeaponType)
|
if (weaptype != mWeaponType)
|
||||||
{
|
{
|
||||||
forcestateupdate = true;
|
forcestateupdate = true;
|
||||||
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
bool useShieldAnims = mAnimation->useShieldAnimations();
|
||||||
|
if (!useShieldAnims)
|
||||||
|
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
||||||
|
|
||||||
weapgroup = getWeaponAnimation(weaptype);
|
weapgroup = getWeaponAnimation(weaptype);
|
||||||
|
|
||||||
// Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly,
|
// Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly,
|
||||||
@ -1316,8 +1334,16 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
|||||||
if (weaptype != ESM::Weapon::None)
|
if (weaptype != ESM::Weapon::None)
|
||||||
{
|
{
|
||||||
mAnimation->showWeapons(false);
|
mAnimation->showWeapons(false);
|
||||||
mAnimation->play(weapgroup, priorityWeapon,
|
int equipMask = MWRender::Animation::BlendMask_All;
|
||||||
MWRender::Animation::BlendMask_All, true,
|
if (useShieldAnims && weaptype != ESM::Weapon::Spell)
|
||||||
|
{
|
||||||
|
equipMask = equipMask |~MWRender::Animation::BlendMask_LeftArm;
|
||||||
|
mAnimation->play("shield", Priority_Block,
|
||||||
|
MWRender::Animation::BlendMask_LeftArm, true,
|
||||||
|
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAnimation->play(weapgroup, priorityWeapon, equipMask, true,
|
||||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
mUpperBodyState = UpperCharState_EquipingWeap;
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ ActorAnimation::~ActorAnimation()
|
|||||||
}
|
}
|
||||||
|
|
||||||
mScabbard.reset();
|
mScabbard.reset();
|
||||||
|
mHolsteredShield.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)
|
PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)
|
||||||
@ -83,6 +84,163 @@ PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::st
|
|||||||
return PartHolderPtr(new PartHolder(instance));
|
return PartHolderPtr(new PartHolder(instance));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ActorAnimation::getShieldMesh(MWWorld::ConstPtr shield) const
|
||||||
|
{
|
||||||
|
std::string mesh = shield.getClass().getModel(shield);
|
||||||
|
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) && weaptype != ESM::Weapon::Spell)
|
||||||
|
{
|
||||||
|
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 (shield != inv.end() && shield->getTypeName() == typeid(ESM::Armor).name() && !getShieldMesh(*shield).empty())
|
||||||
|
{
|
||||||
|
if(stats.getDrawState() != MWMechanics::DrawState_Weapon)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (weapon != inv.end())
|
||||||
|
{
|
||||||
|
const std::string &type = weapon->getTypeName();
|
||||||
|
if(type == typeid(ESM::Weapon).name())
|
||||||
|
{
|
||||||
|
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 == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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->getTypeName() != typeid(ESM::Armor).name())
|
||||||
|
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;
|
||||||
|
|
||||||
|
const std::string &type = weapon->getTypeName();
|
||||||
|
if(type == typeid(ESM::Weapon).name())
|
||||||
|
{
|
||||||
|
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 = getShieldMesh(*shield);
|
||||||
|
if (mesh.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAlpha != 1.f)
|
||||||
|
mResourceSystem->getSceneManager()->recreateShaders(mHolsteredShield->getNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
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->getTypeName() == typeid(ESM::Armor).name() &&
|
||||||
|
!getShieldMesh(*shield).empty())
|
||||||
|
{
|
||||||
|
const std::string &type = weapon->getTypeName();
|
||||||
|
if(type == typeid(ESM::Weapon).name())
|
||||||
|
{
|
||||||
|
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 == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
osg::Group* ActorAnimation::getBoneByName(const std::string& boneName)
|
osg::Group* ActorAnimation::getBoneByName(const std::string& boneName)
|
||||||
{
|
{
|
||||||
if (!mObjectRoot)
|
if (!mObjectRoot)
|
||||||
|
@ -38,11 +38,15 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
|
|||||||
virtual void itemAdded(const MWWorld::ConstPtr& item, int count);
|
virtual void itemAdded(const MWWorld::ConstPtr& item, int count);
|
||||||
virtual void itemRemoved(const MWWorld::ConstPtr& item, int count);
|
virtual void itemRemoved(const MWWorld::ConstPtr& item, int count);
|
||||||
virtual bool isArrowAttached() const { return false; }
|
virtual bool isArrowAttached() const { return false; }
|
||||||
|
virtual bool useShieldAnimations() const;
|
||||||
|
bool updateCarriedLeftVisible(const int weaptype) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
osg::Group* getBoneByName(const std::string& boneName);
|
osg::Group* getBoneByName(const std::string& boneName);
|
||||||
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
|
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
|
||||||
|
virtual void updateHolsteredShield(bool showCarriedLeft);
|
||||||
virtual void updateQuiver();
|
virtual void updateQuiver();
|
||||||
|
virtual std::string getShieldMesh(MWWorld::ConstPtr shield) const;
|
||||||
virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
|
virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
|
||||||
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
|
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
|
||||||
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename)
|
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename)
|
||||||
@ -52,6 +56,7 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
|
|||||||
};
|
};
|
||||||
|
|
||||||
PartHolderPtr mScabbard;
|
PartHolderPtr mScabbard;
|
||||||
|
PartHolderPtr mHolsteredShield;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight);
|
void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight);
|
||||||
|
@ -153,6 +153,8 @@ public:
|
|||||||
|
|
||||||
void setTextKeyListener(TextKeyListener* listener);
|
void setTextKeyListener(TextKeyListener* listener);
|
||||||
|
|
||||||
|
virtual bool updateCarriedLeftVisible(const int weaptype) const { return false; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class AnimationTime : public SceneUtil::ControllerSource
|
class AnimationTime : public SceneUtil::ControllerSource
|
||||||
{
|
{
|
||||||
@ -453,6 +455,7 @@ public:
|
|||||||
/// @note The matching is case-insensitive.
|
/// @note The matching is case-insensitive.
|
||||||
const osg::Node* getNode(const std::string& name) const;
|
const osg::Node* getNode(const std::string& name) const;
|
||||||
|
|
||||||
|
virtual bool useShieldAnimations() const { return false; }
|
||||||
virtual void showWeapons(bool showWeapon) {}
|
virtual void showWeapons(bool showWeapon) {}
|
||||||
virtual void showCarriedLeft(bool show) {}
|
virtual void showCarriedLeft(bool show) {}
|
||||||
virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {}
|
virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {}
|
||||||
|
@ -90,6 +90,7 @@ void CreatureWeaponAnimation::updateParts()
|
|||||||
|
|
||||||
updateHolsteredWeapon(!mShowWeapons);
|
updateHolsteredWeapon(!mShowWeapons);
|
||||||
updateQuiver();
|
updateQuiver();
|
||||||
|
updateHolsteredShield(mShowCarriedLeft);
|
||||||
|
|
||||||
if (mShowWeapons)
|
if (mShowWeapons)
|
||||||
updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);
|
updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <components/nifosg/nifloader.hpp> // TextKeyMapHolder
|
#include <components/nifosg/nifloader.hpp> // TextKeyMapHolder
|
||||||
|
|
||||||
|
#include <components/vfs/manager.hpp>
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
@ -511,6 +513,55 @@ void NpcAnimation::updateNpcBase()
|
|||||||
mWeaponAnimationTime->updateStartTime();
|
mWeaponAnimationTime->updateStartTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string NpcAnimation::getShieldMesh(MWWorld::ConstPtr shield) const
|
||||||
|
{
|
||||||
|
std::string mesh = shield.getClass().getModel(shield);
|
||||||
|
const ESM::Armor *armor = shield.get<ESM::Armor>()->mBase;
|
||||||
|
std::vector<ESM::PartReference> bodyparts = armor->mParts.mParts;
|
||||||
|
if (!bodyparts.empty())
|
||||||
|
{
|
||||||
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
||||||
|
|
||||||
|
// For NPCs try to get shield model from bodyparts first, with ground model as fallback
|
||||||
|
for (auto & part : bodyparts)
|
||||||
|
{
|
||||||
|
if (part.mPart != ESM::PRT_Shield)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string bodypartName;
|
||||||
|
if (!mNpc->isMale() && !part.mFemale.empty())
|
||||||
|
bodypartName = part.mFemale;
|
||||||
|
else if (!part.mMale.empty())
|
||||||
|
bodypartName = part.mMale;
|
||||||
|
|
||||||
|
if (!bodypartName.empty())
|
||||||
|
{
|
||||||
|
const ESM::BodyPart *bodypart = 0;
|
||||||
|
bodypart = partStore.search(bodypartName);
|
||||||
|
if (bodypart->mData.mType != ESM::BodyPart::MT_Armor)
|
||||||
|
return "";
|
||||||
|
else if (!bodypart->mModel.empty())
|
||||||
|
mesh = "meshes\\" + bodypart->mModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void NpcAnimation::updateParts()
|
void NpcAnimation::updateParts()
|
||||||
{
|
{
|
||||||
if (!mObjectRoot.get())
|
if (!mObjectRoot.get())
|
||||||
@ -954,6 +1005,8 @@ void NpcAnimation::showCarriedLeft(bool show)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
removeIndividualPart(ESM::PRT_Shield);
|
removeIndividualPart(ESM::PRT_Shield);
|
||||||
|
|
||||||
|
updateHolsteredShield(mShowCarriedLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpcAnimation::attachArrow()
|
void NpcAnimation::attachArrow()
|
||||||
@ -1051,6 +1104,14 @@ void NpcAnimation::setWeaponGroup(const std::string &group, bool relativeDuratio
|
|||||||
|
|
||||||
void NpcAnimation::equipmentChanged()
|
void NpcAnimation::equipmentChanged()
|
||||||
{
|
{
|
||||||
|
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
|
||||||
|
if (shieldSheathing)
|
||||||
|
{
|
||||||
|
int weaptype;
|
||||||
|
MWMechanics::getActiveWeapon(mPtr, &weaptype);
|
||||||
|
showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
||||||
|
}
|
||||||
|
|
||||||
updateParts();
|
updateParts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
virtual void addControllers();
|
virtual void addControllers();
|
||||||
virtual bool isArrowAttached() const;
|
virtual bool isArrowAttached() const;
|
||||||
|
virtual std::string getShieldMesh(MWWorld::ConstPtr shield) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -184,6 +184,22 @@ Otherwise they wait for the enemies or the player to do an attack first.
|
|||||||
Please note this setting has not been extensively tested and could have side effects with certain quests.
|
Please note this setting has not been extensively tested and could have side effects with certain quests.
|
||||||
This setting can be toggled in Advanced tab of the launcher.
|
This setting can be toggled in Advanced tab of the launcher.
|
||||||
|
|
||||||
|
shield sheathing
|
||||||
|
----------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
If this setting is true, OpenMW will utilize shield sheathing-compatible assets to display holstered shields.
|
||||||
|
|
||||||
|
To make use of this, you need to have an xbase_anim_sh.nif file with weapon bones that will be injected into the skeleton.
|
||||||
|
Also you can use additional _sh meshes for more precise shield placement.
|
||||||
|
Warning: this feature may conflict with mods that use pseudo-shields to emulate item in actor's hand (e.g. books, baskets, pick axes).
|
||||||
|
To avoid conflicts, you can use _sh mesh without "Bip01 Sheath" node for such "shields" meshes, or declare its bodypart as Clothing type, not as Armor.
|
||||||
|
Also you can use an _sh node with empty "Bip01 Sheath" node.
|
||||||
|
In this case the engine will use basic shield model, but will use transformations from the "Bip01 Sheath" node.
|
||||||
|
|
||||||
weapon sheathing
|
weapon sheathing
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
@ -255,6 +255,9 @@ strength influences hand to hand = 0
|
|||||||
# Render holstered weapons (with quivers and scabbards), requires modded assets
|
# Render holstered weapons (with quivers and scabbards), requires modded assets
|
||||||
weapon sheathing = false
|
weapon sheathing = false
|
||||||
|
|
||||||
|
# Render holstered shield when it is not in actor's hands, requires modded assets
|
||||||
|
shield sheathing = false
|
||||||
|
|
||||||
# Allow non-standard ammunition solely to bypass normal weapon resistance or weakness
|
# Allow non-standard ammunition solely to bypass normal weapon resistance or weakness
|
||||||
only appropriate ammunition bypasses resistance = false
|
only appropriate ammunition bypasses resistance = false
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user