#include "weaponanimation.hpp" #include #include #include #include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "animation.hpp" namespace MWRender { float WeaponAnimationTime::getValue() const { 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) { mWeaponGroup = group; mStartTime = mAnimation->getStartTime(mWeaponGroup); } void WeaponAnimationTime::updateStartTime() { setGroup(mWeaponGroup); } void WeaponAnimation::attachArrow(MWWorld::Ptr actor) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weaponSlot != inv.end() && weaponSlot->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) showWeapon(true); else { NifOgre::ObjectScenePtr weapon = getWeapon(); MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) return; std::string model = ammo->getClass().getModel(*ammo); mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", weapon->mSkelBase->getParentSceneNode(), model); configureAddedObject(mAmmunition, *ammo, MWWorld::InventoryStore::Slot_Ammunition); } } void WeaponAnimation::releaseArrow(MWWorld::Ptr actor) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weapon == inv.end()) return; // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise. Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); // Reduce fatigue // somewhat of a guess, but using the weapon weight makes sense const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); MWMechanics::CreatureStats& attackerStats = actor.getClass().getCreatureStats(actor); MWMechanics::DynamicStat fatigue = attackerStats.getFatigue(); const float normalizedEncumbrance = actor.getClass().getEncumbrance(actor) / actor.getClass().getCapacity(actor); float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; if (!weapon->isEmpty()) fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult; fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); attackerStats.setFatigue(fatigue); if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) { // Thrown weapons get detached now NifOgre::ObjectScenePtr objects = getWeapon(); Ogre::Vector3 launchPos(0,0,0); if (objects->mSkelBase) { launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition(); } else if (objects->mEntities.size()) { objects->mEntities[0]->getParentNode()->needUpdate(true); launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition(); } float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * actor.getClass().getCreatureStats(actor).getAttackStrength(); MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed); showWeapon(false); inv.remove(*weapon, 1, actor); } 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; Ogre::Vector3 launchPos(0,0,0); if (mAmmunition->mSkelBase) { launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition(); } else if (mAmmunition->mEntities.size()) { mAmmunition->mEntities[0]->getParentNode()->needUpdate(true); launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition(); } float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * actor.getClass().getCreatureStats(actor).getAttackStrength(); MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed); inv.remove(*ammo, 1, actor); mAmmunition.setNull(); } } void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel) { if (mPitchFactor == 0) return; float pitch = xrot * mPitchFactor; Ogre::Node *node; // In spherearcher.nif, we have spine, not Spine. Not sure if all bone names should be case insensitive? if (skel->hasBone("Bip01 spine2")) node = skel->getBone("Bip01 spine2"); else node = skel->getBone("Bip01 Spine2"); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); if (skel->hasBone("Bip01 spine1")) // in spherearcher.nif node = skel->getBone("Bip01 spine1"); else node = skel->getBone("Bip01 Spine1"); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); } }