#include "weaponanimation.hpp" #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/weapontype.hpp" #include "animation.hpp" #include "rotatecontroller.hpp" namespace MWRender { float WeaponAnimationTime::getValue(osg::NodeVisitor*) { if (mWeaponGroup.empty()) return 0; float current = mAnimation->getCurrentTime(mWeaponGroup); if (current == -1) return 0; return current - mStartTime; } void WeaponAnimationTime::setGroup(const std::string& group, bool relativeTime) { mWeaponGroup = group; mRelativeTime = relativeTime; if (mRelativeTime) mStartTime = mAnimation->getStartTime(mWeaponGroup); else mStartTime = 0; } void WeaponAnimationTime::updateStartTime() { setGroup(mWeaponGroup, mRelativeTime); } WeaponAnimation::WeaponAnimation() : mPitchFactor(0) { } WeaponAnimation::~WeaponAnimation() {} void WeaponAnimation::attachArrow(const MWWorld::Ptr& actor) { const MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ConstContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weaponSlot == inv.end()) return; if (weaponSlot->getType() != ESM::Weapon::sRecordId) return; int type = weaponSlot->get()->mBase->mData.mType; ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass; if (weapclass == ESM::WeaponType::Thrown) { const auto& soundid = weaponSlot->getClass().getUpSoundId(*weaponSlot); if (!soundid.empty()) { MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(actor, soundid, 1.0f, 1.0f); } showWeapon(true); } else if (weapclass == ESM::WeaponType::Ranged) { osg::Group* parent = getArrowBone(); if (!parent) return; MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) return; VFS::Path::Normalized model(ammo->getClass().getCorrectedModel(*ammo)); osg::ref_ptr arrow = getResourceSystem()->getSceneManager()->getInstance(model, parent); mAmmunition = std::make_unique(arrow); } } void WeaponAnimation::detachArrow(MWWorld::Ptr actor) { mAmmunition.reset(); } void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weapon == inv.end()) return; if (weapon->getType() != ESM::Weapon::sRecordId) return; // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's // orientation dictates otherwise. osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, -1)); const MWWorld::Store& gmst = MWBase::Environment::get().getESMStore()->get(); MWMechanics::applyFatigueLoss(actor, *weapon, attackStrength); if (MWMechanics::getWeaponType(weapon->get()->mBase->mData.mType)->mWeaponClass == ESM::WeaponType::Thrown) { // Thrown weapons get detached now osg::Node* weaponNode = getWeaponNode(); if (!weaponNode) return; osg::NodePathList nodepaths = weaponNode->getParentalNodePaths(); if (nodepaths.empty()) return; osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans(); float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat(); float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat(); float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength; MWWorld::Ptr weaponPtr = *weapon; MWBase::Environment::get().getWorld()->launchProjectile( actor, weaponPtr, launchPos, orient, weaponPtr, speed, attackStrength); showWeapon(false); inv.remove(*weapon, 1); } else { // With bows and crossbows only the used arrow/bolt gets detached MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) return; if (!mAmmunition) return; osg::ref_ptr ammoNode = mAmmunition->getNode(); osg::NodePathList nodepaths = ammoNode->getParentalNodePaths(); if (nodepaths.empty()) return; osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans(); float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->mValue.getFloat(); float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat(); float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength; MWWorld::Ptr weaponPtr = *weapon; MWWorld::Ptr ammoPtr = *ammo; MWBase::Environment::get().getWorld()->launchProjectile( actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength); inv.remove(ammoPtr, 1); mAmmunition.reset(); } } void WeaponAnimation::addControllers(const Animation::NodeMap& nodes, std::vector, osg::ref_ptr>>& map, osg::Node* objectRoot) { for (int i = 0; i < 2; ++i) { mSpineControllers[i] = nullptr; Animation::NodeMap::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2"); if (found != nodes.end()) { osg::Node* node = found->second; mSpineControllers[i] = new RotateController(objectRoot); node->addUpdateCallback(mSpineControllers[i]); map.emplace_back(node, mSpineControllers[i]); } } } void WeaponAnimation::deleteControllers() { for (int i = 0; i < 2; ++i) mSpineControllers[i] = nullptr; } void WeaponAnimation::configureControllers(float characterPitchRadians) { if (mPitchFactor == 0.f || characterPitchRadians == 0.f) { setControllerEnabled(false); return; } float pitch = characterPitchRadians * mPitchFactor; osg::Quat rotate(pitch / 2, osg::Vec3f(-1, 0, 0)); setControllerRotate(rotate); setControllerEnabled(true); } void WeaponAnimation::setControllerRotate(const osg::Quat& rotate) { for (int i = 0; i < 2; ++i) if (mSpineControllers[i]) mSpineControllers[i]->setRotate(rotate); } void WeaponAnimation::setControllerEnabled(bool enabled) { for (int i = 0; i < 2; ++i) if (mSpineControllers[i]) mSpineControllers[i]->setEnabled(enabled); } }