1 #include "weaponanimation.hpp"
2 
3 #include <osg/MatrixTransform>
4 
5 #include <components/resource/resourcesystem.hpp>
6 #include <components/resource/scenemanager.hpp>
7 
8 #include "../mwbase/world.hpp"
9 #include "../mwbase/environment.hpp"
10 #include "../mwbase/soundmanager.hpp"
11 
12 #include "../mwworld/inventorystore.hpp"
13 #include "../mwworld/class.hpp"
14 #include "../mwworld/esmstore.hpp"
15 
16 #include "../mwmechanics/creaturestats.hpp"
17 #include "../mwmechanics/combat.hpp"
18 #include "../mwmechanics/weapontype.hpp"
19 
20 #include "animation.hpp"
21 #include "rotatecontroller.hpp"
22 
23 namespace MWRender
24 {
25 
getValue(osg::NodeVisitor *)26 float WeaponAnimationTime::getValue(osg::NodeVisitor*)
27 {
28     if (mWeaponGroup.empty())
29         return 0;
30 
31     float current = mAnimation->getCurrentTime(mWeaponGroup);
32     if (current == -1)
33         return 0;
34     return current - mStartTime;
35 }
36 
setGroup(const std::string & group,bool relativeTime)37 void WeaponAnimationTime::setGroup(const std::string &group, bool relativeTime)
38 {
39     mWeaponGroup = group;
40     mRelativeTime = relativeTime;
41 
42     if (mRelativeTime)
43         mStartTime = mAnimation->getStartTime(mWeaponGroup);
44     else
45         mStartTime = 0;
46 }
47 
updateStartTime()48 void WeaponAnimationTime::updateStartTime()
49 {
50     setGroup(mWeaponGroup, mRelativeTime);
51 }
52 
WeaponAnimation()53 WeaponAnimation::WeaponAnimation()
54     : mPitchFactor(0)
55 {
56 }
57 
~WeaponAnimation()58 WeaponAnimation::~WeaponAnimation()
59 {
60 
61 }
62 
attachArrow(MWWorld::Ptr actor)63 void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
64 {
65     const MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
66     MWWorld::ConstContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
67     if (weaponSlot == inv.end())
68         return;
69     if (weaponSlot->getTypeName() != typeid(ESM::Weapon).name())
70         return;
71 
72     int type = weaponSlot->get<ESM::Weapon>()->mBase->mData.mType;
73     ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;
74     if (weapclass == ESM::WeaponType::Thrown)
75     {
76         std::string soundid = weaponSlot->getClass().getUpSoundId(*weaponSlot);
77         if(!soundid.empty())
78         {
79             MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
80             sndMgr->playSound3D(actor, soundid, 1.0f, 1.0f);
81         }
82         showWeapon(true);
83     }
84     else if (weapclass == ESM::WeaponType::Ranged)
85     {
86         osg::Group* parent = getArrowBone();
87         if (!parent)
88             return;
89 
90         MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
91         if (ammo == inv.end())
92             return;
93         std::string model = ammo->getClass().getModel(*ammo);
94 
95         osg::ref_ptr<osg::Node> arrow = getResourceSystem()->getSceneManager()->getInstance(model, parent);
96 
97         mAmmunition = PartHolderPtr(new PartHolder(arrow));
98     }
99 }
100 
detachArrow(MWWorld::Ptr actor)101 void WeaponAnimation::detachArrow(MWWorld::Ptr actor)
102 {
103     mAmmunition.reset();
104 }
105 
releaseArrow(MWWorld::Ptr actor,float attackStrength)106 void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
107 {
108     MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
109     MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
110     if (weapon == inv.end())
111         return;
112     if (weapon->getTypeName() != typeid(ESM::Weapon).name())
113         return;
114 
115     // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
116     osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
117             * osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
118 
119     const MWWorld::Store<ESM::GameSetting> &gmst =
120         MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
121 
122     MWMechanics::applyFatigueLoss(actor, *weapon, attackStrength);
123 
124     if (MWMechanics::getWeaponType(weapon->get<ESM::Weapon>()->mBase->mData.mType)->mWeaponClass == ESM::WeaponType::Thrown)
125     {
126         // Thrown weapons get detached now
127         osg::Node* weaponNode = getWeaponNode();
128         if (!weaponNode)
129             return;
130         osg::NodePathList nodepaths = weaponNode->getParentalNodePaths();
131         if (nodepaths.empty())
132             return;
133         osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
134 
135         float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat();
136         float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat();
137         float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength;
138 
139         MWWorld::Ptr weaponPtr = *weapon;
140         MWBase::Environment::get().getWorld()->launchProjectile(actor, weaponPtr, launchPos, orient, weaponPtr, speed, attackStrength);
141 
142         showWeapon(false);
143 
144         inv.remove(*weapon, 1, actor);
145     }
146     else
147     {
148         // With bows and crossbows only the used arrow/bolt gets detached
149         MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
150         if (ammo == inv.end())
151             return;
152 
153         if (!mAmmunition)
154             return;
155 
156         osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode();
157         osg::NodePathList nodepaths = ammoNode->getParentalNodePaths();
158         if (nodepaths.empty())
159             return;
160         osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
161 
162         float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->mValue.getFloat();
163         float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat();
164         float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength;
165 
166         MWWorld::Ptr weaponPtr = *weapon;
167         MWWorld::Ptr ammoPtr = *ammo;
168         MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength);
169 
170         inv.remove(ammoPtr, 1, actor);
171         mAmmunition.reset();
172     }
173 }
174 
addControllers(const std::map<std::string,osg::ref_ptr<osg::MatrixTransform>> & nodes,std::vector<std::pair<osg::ref_ptr<osg::Node>,osg::ref_ptr<osg::NodeCallback>>> & map,osg::Node * objectRoot)175 void WeaponAnimation::addControllers(const std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >& nodes,
176     std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>> &map, osg::Node* objectRoot)
177 {
178     for (int i=0; i<2; ++i)
179     {
180         mSpineControllers[i] = nullptr;
181 
182         std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2");
183         if (found != nodes.end())
184         {
185             osg::Node* node = found->second;
186             mSpineControllers[i] = new RotateController(objectRoot);
187             node->addUpdateCallback(mSpineControllers[i]);
188             map.emplace_back(node, mSpineControllers[i]);
189         }
190     }
191 }
192 
deleteControllers()193 void WeaponAnimation::deleteControllers()
194 {
195     for (int i=0; i<2; ++i)
196         mSpineControllers[i] = nullptr;
197 }
198 
configureControllers(float characterPitchRadians)199 void WeaponAnimation::configureControllers(float characterPitchRadians)
200 {
201     if (mPitchFactor == 0.f || characterPitchRadians == 0.f)
202     {
203         setControllerEnabled(false);
204         return;
205     }
206 
207     float pitch = characterPitchRadians * mPitchFactor;
208     osg::Quat rotate (pitch/2, osg::Vec3f(-1,0,0));
209     setControllerRotate(rotate);
210     setControllerEnabled(true);
211 }
212 
setControllerRotate(const osg::Quat & rotate)213 void WeaponAnimation::setControllerRotate(const osg::Quat& rotate)
214 {
215     for (int i=0; i<2; ++i)
216         if (mSpineControllers[i])
217             mSpineControllers[i]->setRotate(rotate);
218 }
219 
setControllerEnabled(bool enabled)220 void WeaponAnimation::setControllerEnabled(bool enabled)
221 {
222     for (int i=0; i<2; ++i)
223         if (mSpineControllers[i])
224             mSpineControllers[i]->setEnabled(enabled);
225 }
226 
227 }
228