2011-12-12 03:40:00 +00:00
|
|
|
#include "creatureanimation.hpp"
|
2012-07-03 13:32:38 +00:00
|
|
|
|
2017-02-03 22:15:37 +00:00
|
|
|
#include <osg/MatrixTransform>
|
|
|
|
|
2022-01-22 14:58:41 +00:00
|
|
|
#include <components/esm3/loadcrea.hpp>
|
2018-08-14 19:05:43 +00:00
|
|
|
#include <components/debug/debuglog.hpp>
|
2015-04-19 01:05:47 +00:00
|
|
|
#include <components/resource/resourcesystem.hpp>
|
|
|
|
#include <components/resource/scenemanager.hpp>
|
|
|
|
#include <components/sceneutil/attach.hpp>
|
2015-05-30 23:07:43 +00:00
|
|
|
#include <components/sceneutil/visitor.hpp>
|
2015-11-20 20:57:04 +00:00
|
|
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
2016-07-02 18:19:55 +00:00
|
|
|
#include <components/sceneutil/skeleton.hpp>
|
2020-12-30 20:11:32 +00:00
|
|
|
#include <components/settings/settings.hpp>
|
2017-02-03 22:15:37 +00:00
|
|
|
#include <components/misc/stringops.hpp>
|
|
|
|
|
2020-01-10 10:00:57 +00:00
|
|
|
#include "../mwbase/environment.hpp"
|
2012-07-03 10:30:50 +00:00
|
|
|
#include "../mwbase/world.hpp"
|
2011-12-12 03:40:00 +00:00
|
|
|
|
2018-12-26 09:45:28 +00:00
|
|
|
#include "../mwmechanics/weapontype.hpp"
|
|
|
|
|
2014-01-19 12:05:26 +00:00
|
|
|
#include "../mwworld/class.hpp"
|
2020-01-10 10:00:57 +00:00
|
|
|
#include "../mwworld/esmstore.hpp"
|
2014-01-19 12:05:26 +00:00
|
|
|
|
2013-01-19 00:21:29 +00:00
|
|
|
namespace MWRender
|
|
|
|
{
|
2011-12-12 04:42:39 +00:00
|
|
|
|
2015-04-19 01:05:47 +00:00
|
|
|
CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr,
|
|
|
|
const std::string& model, Resource::ResourceSystem* resourceSystem)
|
2016-10-08 01:49:50 +00:00
|
|
|
: ActorAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)
|
2012-07-13 03:12:18 +00:00
|
|
|
{
|
2015-04-23 18:41:31 +00:00
|
|
|
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
|
2011-12-12 03:40:00 +00:00
|
|
|
|
2014-01-19 12:05:26 +00:00
|
|
|
if(!model.empty())
|
2012-07-13 03:12:18 +00:00
|
|
|
{
|
2015-08-15 17:01:21 +00:00
|
|
|
setObjectRoot(model, false, false, true);
|
2014-01-19 12:05:26 +00:00
|
|
|
|
2015-04-23 18:41:31 +00:00
|
|
|
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
|
2020-12-30 20:11:32 +00:00
|
|
|
addAnimSource(Settings::Manager::getString("xbaseanim", "Models"), model);
|
2017-11-10 21:02:43 +00:00
|
|
|
addAnimSource(model, model);
|
2014-01-19 12:05:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-13 03:12:18 +00:00
|
|
|
|
2015-04-19 01:05:47 +00:00
|
|
|
CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem)
|
2016-10-08 01:49:50 +00:00
|
|
|
: ActorAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)
|
2014-01-19 12:31:17 +00:00
|
|
|
, mShowWeapons(false)
|
|
|
|
, mShowCarriedLeft(false)
|
2014-01-19 12:05:26 +00:00
|
|
|
{
|
2015-04-23 18:41:31 +00:00
|
|
|
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
|
2014-01-19 12:05:26 +00:00
|
|
|
|
|
|
|
if(!model.empty())
|
|
|
|
{
|
2015-08-15 17:01:21 +00:00
|
|
|
setObjectRoot(model, true, false, true);
|
2013-05-07 23:59:32 +00:00
|
|
|
|
2015-04-23 18:41:31 +00:00
|
|
|
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
|
2018-03-03 10:16:21 +00:00
|
|
|
{
|
2020-12-30 20:11:32 +00:00
|
|
|
addAnimSource(Settings::Manager::getString("xbaseanim", "Models"), model);
|
2018-03-03 10:16:21 +00:00
|
|
|
}
|
2017-11-10 21:02:43 +00:00
|
|
|
addAnimSource(model, model);
|
2014-01-19 12:05:26 +00:00
|
|
|
|
2016-10-08 01:49:50 +00:00
|
|
|
mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr);
|
2014-01-19 12:05:26 +00:00
|
|
|
|
|
|
|
updateParts();
|
|
|
|
}
|
2014-03-12 10:30:44 +00:00
|
|
|
|
2022-04-03 21:23:25 +00:00
|
|
|
mWeaponAnimationTime = std::make_shared<WeaponAnimationTime>(this);
|
2014-01-19 12:05:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CreatureWeaponAnimation::showWeapons(bool showWeapon)
|
|
|
|
{
|
|
|
|
if (showWeapon != mShowWeapons)
|
|
|
|
{
|
|
|
|
mShowWeapons = showWeapon;
|
|
|
|
updateParts();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreatureWeaponAnimation::showCarriedLeft(bool show)
|
|
|
|
{
|
|
|
|
if (show != mShowCarriedLeft)
|
|
|
|
{
|
|
|
|
mShowCarriedLeft = show;
|
|
|
|
updateParts();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreatureWeaponAnimation::updateParts()
|
|
|
|
{
|
2016-06-07 00:42:57 +00:00
|
|
|
mAmmunition.reset();
|
2015-04-19 01:05:47 +00:00
|
|
|
mWeapon.reset();
|
|
|
|
mShield.reset();
|
2014-01-19 12:05:26 +00:00
|
|
|
|
2018-03-03 10:16:21 +00:00
|
|
|
updateHolsteredWeapon(!mShowWeapons);
|
|
|
|
updateQuiver();
|
2019-08-11 11:01:48 +00:00
|
|
|
updateHolsteredShield(mShowCarriedLeft);
|
2018-03-03 10:16:21 +00:00
|
|
|
|
2014-01-19 12:05:26 +00:00
|
|
|
if (mShowWeapons)
|
|
|
|
updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);
|
|
|
|
if (mShowCarriedLeft)
|
|
|
|
updatePart(mShield, MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
|
|
}
|
|
|
|
|
2015-04-19 01:05:47 +00:00
|
|
|
void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
|
2014-01-19 12:05:26 +00:00
|
|
|
{
|
2015-04-19 01:05:47 +00:00
|
|
|
if (!mObjectRoot)
|
2015-03-02 14:19:57 +00:00
|
|
|
return;
|
|
|
|
|
2017-02-28 14:31:51 +00:00
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator it = inv.getSlot(slot);
|
2014-01-19 12:05:26 +00:00
|
|
|
|
|
|
|
if (it == inv.end())
|
|
|
|
{
|
2015-04-19 01:05:47 +00:00
|
|
|
scene.reset();
|
2014-01-19 12:05:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-02-28 14:31:51 +00:00
|
|
|
MWWorld::ConstPtr item = *it;
|
2014-01-19 12:05:26 +00:00
|
|
|
|
|
|
|
std::string bonename;
|
2020-01-10 10:00:57 +00:00
|
|
|
std::string itemModel = item.getClass().getModel(item);
|
2014-01-19 12:05:26 +00:00
|
|
|
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
|
2018-12-26 09:45:28 +00:00
|
|
|
{
|
2021-10-11 11:46:21 +00:00
|
|
|
if(item.getType() == ESM::Weapon::sRecordId)
|
2018-12-26 09:45:28 +00:00
|
|
|
{
|
|
|
|
int type = item.get<ESM::Weapon>()->mBase->mData.mType;
|
|
|
|
bonename = MWMechanics::getWeaponType(type)->mAttachBone;
|
2019-08-09 06:00:40 +00:00
|
|
|
if (bonename != "Weapon Bone")
|
|
|
|
{
|
|
|
|
const NodeMap& nodeMap = getNodeMap();
|
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
|
|
|
NodeMap::const_iterator found = nodeMap.find(bonename);
|
2019-08-09 06:00:40 +00:00
|
|
|
if (found == nodeMap.end())
|
|
|
|
bonename = "Weapon Bone";
|
|
|
|
}
|
2018-12-26 09:45:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
bonename = "Weapon Bone";
|
|
|
|
}
|
2014-01-19 12:05:26 +00:00
|
|
|
else
|
2020-01-10 10:00:57 +00:00
|
|
|
{
|
2014-01-19 12:05:26 +00:00
|
|
|
bonename = "Shield Bone";
|
2021-10-11 11:46:21 +00:00
|
|
|
if (item.getType() == ESM::Armor::sRecordId)
|
2020-01-10 10:00:57 +00:00
|
|
|
{
|
2021-11-01 18:33:29 +00:00
|
|
|
itemModel = getShieldMesh(item, false);
|
2020-01-10 10:00:57 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-19 12:05:26 +00:00
|
|
|
|
2017-02-03 22:15:37 +00:00
|
|
|
try
|
2014-03-12 10:30:44 +00:00
|
|
|
{
|
2021-11-02 17:01:22 +00:00
|
|
|
osg::ref_ptr<osg::Node> attached = attach(itemModel, bonename, bonename, item.getType() == ESM::Light::sRecordId);
|
2017-02-03 22:15:37 +00:00
|
|
|
|
|
|
|
scene.reset(new PartHolder(attached));
|
|
|
|
|
|
|
|
if (!item.getClass().getEnchantment(item).empty())
|
2019-08-07 07:03:26 +00:00
|
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, item.getClass().getEnchantmentColor(item));
|
2017-02-03 22:15:37 +00:00
|
|
|
|
|
|
|
// Crossbows start out with a bolt attached
|
|
|
|
// FIXME: code duplicated from NpcAnimation
|
|
|
|
if (slot == MWWorld::InventoryStore::Slot_CarriedRight &&
|
2021-10-11 11:46:21 +00:00
|
|
|
item.getType() == ESM::Weapon::sRecordId &&
|
2017-02-03 22:15:37 +00:00
|
|
|
item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
|
|
|
{
|
2018-12-26 09:45:28 +00:00
|
|
|
const ESM::WeaponType* weaponInfo = MWMechanics::getWeaponType(ESM::Weapon::MarksmanCrossbow);
|
2017-02-28 14:31:51 +00:00
|
|
|
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
2018-12-26 09:45:28 +00:00
|
|
|
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponInfo->mAmmoType)
|
2017-02-03 22:15:37 +00:00
|
|
|
attachArrow();
|
|
|
|
else
|
|
|
|
mAmmunition.reset();
|
|
|
|
}
|
2015-05-30 23:07:43 +00:00
|
|
|
else
|
|
|
|
mAmmunition.reset();
|
2014-03-12 10:30:44 +00:00
|
|
|
|
2017-05-05 17:26:09 +00:00
|
|
|
std::shared_ptr<SceneUtil::ControllerSource> source;
|
2015-05-30 23:07:43 +00:00
|
|
|
|
2017-02-03 22:15:37 +00:00
|
|
|
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
|
|
|
|
source = mWeaponAnimationTime;
|
|
|
|
else
|
|
|
|
source.reset(new NullAnimationTime);
|
2015-05-30 23:07:43 +00:00
|
|
|
|
2017-02-03 22:15:37 +00:00
|
|
|
SceneUtil::AssignControllerSourcesVisitor assignVisitor(source);
|
|
|
|
attached->accept(assignVisitor);
|
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
2018-08-14 19:05:43 +00:00
|
|
|
Log(Debug::Error) << "Can not add creature part: " << e.what();
|
2017-02-03 22:15:37 +00:00
|
|
|
}
|
2011-12-12 03:40:00 +00:00
|
|
|
}
|
2011-12-26 03:37:26 +00:00
|
|
|
|
2018-03-03 10:16:21 +00:00
|
|
|
bool CreatureWeaponAnimation::isArrowAttached() const
|
|
|
|
{
|
|
|
|
return mAmmunition != nullptr;
|
|
|
|
}
|
|
|
|
|
2020-08-28 11:28:26 +00:00
|
|
|
void CreatureWeaponAnimation::detachArrow()
|
|
|
|
{
|
|
|
|
WeaponAnimation::detachArrow(mPtr);
|
|
|
|
updateQuiver();
|
|
|
|
}
|
|
|
|
|
2014-03-12 10:30:44 +00:00
|
|
|
void CreatureWeaponAnimation::attachArrow()
|
|
|
|
{
|
2015-05-30 23:07:43 +00:00
|
|
|
WeaponAnimation::attachArrow(mPtr);
|
2019-08-07 05:45:10 +00:00
|
|
|
|
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
|
|
|
if (ammo != inv.end() && !ammo->getClass().getEnchantment(*ammo).empty())
|
|
|
|
{
|
|
|
|
osg::Group* bone = getArrowBone();
|
|
|
|
if (bone != nullptr && bone->getNumChildren())
|
2019-08-07 07:03:26 +00:00
|
|
|
SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo));
|
2019-08-07 05:45:10 +00:00
|
|
|
}
|
|
|
|
|
2018-03-03 10:16:21 +00:00
|
|
|
updateQuiver();
|
2014-03-12 10:30:44 +00:00
|
|
|
}
|
|
|
|
|
2015-06-26 03:15:07 +00:00
|
|
|
void CreatureWeaponAnimation::releaseArrow(float attackStrength)
|
2014-03-12 10:30:44 +00:00
|
|
|
{
|
2015-06-26 03:15:07 +00:00
|
|
|
WeaponAnimation::releaseArrow(mPtr, attackStrength);
|
2018-03-03 10:16:21 +00:00
|
|
|
updateQuiver();
|
2015-05-30 23:07:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
osg::Group *CreatureWeaponAnimation::getArrowBone()
|
|
|
|
{
|
|
|
|
if (!mWeapon)
|
2018-10-09 06:21:12 +00:00
|
|
|
return nullptr;
|
2015-05-30 23:07:43 +00:00
|
|
|
|
2018-12-26 09:45:28 +00:00
|
|
|
if (!mPtr.getClass().hasInventoryStore(mPtr))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
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-12-26 09:45:28 +00:00
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
|
|
|
int ammoType = MWMechanics::getWeaponType(type)->mAmmoType;
|
2022-05-02 19:11:01 +00:00
|
|
|
if (ammoType == ESM::Weapon::None)
|
|
|
|
return nullptr;
|
2018-12-26 09:45:28 +00:00
|
|
|
|
2020-09-09 06:14:26 +00:00
|
|
|
// Try to find and attachment bone in actor's skeleton, otherwise fall back to the ArrowBone in weapon's mesh
|
|
|
|
osg::Group* bone = getBoneByName(MWMechanics::getWeaponType(ammoType)->mAttachBone);
|
|
|
|
if (bone == nullptr)
|
|
|
|
{
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor ("ArrowBone");
|
|
|
|
mWeapon->getNode()->accept(findVisitor);
|
|
|
|
bone = findVisitor.mFoundNode;
|
|
|
|
}
|
|
|
|
return bone;
|
2015-05-30 23:07:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
osg::Node *CreatureWeaponAnimation::getWeaponNode()
|
|
|
|
{
|
2018-10-09 06:21:12 +00:00
|
|
|
return mWeapon ? mWeapon->getNode().get() : nullptr;
|
2015-05-30 23:07:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Resource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem()
|
|
|
|
{
|
|
|
|
return mResourceSystem;
|
2014-03-12 10:30:44 +00:00
|
|
|
}
|
|
|
|
|
2015-05-31 16:53:16 +00:00
|
|
|
void CreatureWeaponAnimation::addControllers()
|
|
|
|
{
|
2015-06-22 19:06:27 +00:00
|
|
|
Animation::addControllers();
|
2015-05-31 16:53:16 +00:00
|
|
|
WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
|
|
|
|
}
|
|
|
|
|
2015-04-19 01:05:47 +00:00
|
|
|
osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration)
|
2014-03-12 10:30:44 +00:00
|
|
|
{
|
2015-05-21 23:54:06 +00:00
|
|
|
osg::Vec3f ret = Animation::runAnimation(duration);
|
2015-03-02 14:19:57 +00:00
|
|
|
|
2020-07-09 06:47:37 +00:00
|
|
|
WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians());
|
2014-06-27 01:11:37 +00:00
|
|
|
|
2015-05-21 23:54:06 +00:00
|
|
|
return ret;
|
2014-03-12 10:30:44 +00:00
|
|
|
}
|
|
|
|
|
2012-01-17 14:10:53 +00:00
|
|
|
}
|