forked from teamnwah/openmw-tes3coop
Fixed ranged combat for creatures
This commit is contained in:
parent
a9dcc90970
commit
0cd40294a2
7 changed files with 307 additions and 157 deletions
|
@ -15,7 +15,7 @@ add_openmw_dir (mwrender
|
|||
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||
actors objects renderinginterface localmap occlusionquery water shadows
|
||||
characterpreview globalmap videoplayer ripplesimulation refraction
|
||||
terrainstorage renderconst effectmanager
|
||||
terrainstorage renderconst effectmanager weaponanimation
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
|
|
@ -55,6 +55,8 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr)
|
|||
|
||||
updateParts();
|
||||
}
|
||||
|
||||
mWeaponAnimationTime = Ogre::SharedPtr<WeaponAnimationTime>(new WeaponAnimationTime(this));
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::showWeapons(bool showWeapon)
|
||||
|
@ -110,6 +112,20 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
|
|||
setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0,
|
||||
!item.getClass().getEnchantment(item).empty(), &glowColor);
|
||||
|
||||
// Crossbows start out with a bolt attached
|
||||
if (slot == MWWorld::InventoryStore::Slot_CarriedRight &&
|
||||
item.getTypeName() == typeid(ESM::Weapon).name() &&
|
||||
item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
||||
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)
|
||||
attachArrow();
|
||||
else
|
||||
mAmmunition.setNull();
|
||||
}
|
||||
else
|
||||
mAmmunition.setNull();
|
||||
|
||||
if(scene->mSkelBase)
|
||||
{
|
||||
Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton();
|
||||
|
@ -133,15 +149,42 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
|
|||
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// type == ESM::PRT_Weapon should get an animation source based on the current offset
|
||||
// of the weapon attack animation (from its beginning, or start marker?)
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(scene->mControllers.begin());
|
||||
for(;ctrl != scene->mControllers.end();ctrl++)
|
||||
{
|
||||
if(ctrl->getSource().isNull())
|
||||
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime()));
|
||||
{
|
||||
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
|
||||
ctrl->setSource(mWeaponAnimationTime);
|
||||
else
|
||||
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
|
||||
{
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
|
||||
|
||||
setRenderProperties(object, RV_Actors, RQG_Main, RQG_Alpha, 0,
|
||||
!ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::attachArrow()
|
||||
{
|
||||
WeaponAnimation::attachArrow(mPtr);
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::releaseArrow()
|
||||
{
|
||||
WeaponAnimation::releaseArrow(mPtr);
|
||||
}
|
||||
|
||||
Ogre::Vector3 CreatureWeaponAnimation::runAnimation(float duration)
|
||||
{
|
||||
Ogre::Vector3 ret = Animation::runAnimation(duration);
|
||||
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], mSkelBase->getSkeleton());
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define GAME_RENDER_CREATUREANIMATION_H
|
||||
|
||||
#include "animation.hpp"
|
||||
#include "weaponanimation.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
|
@ -21,7 +22,7 @@ namespace MWRender
|
|||
// For creatures with weapons and shields
|
||||
// Animation is already virtual anyway, so might as well make a separate class.
|
||||
// Most creatures don't need weapons/shields, so this will save some memory.
|
||||
class CreatureWeaponAnimation : public Animation, public MWWorld::InventoryStoreListener
|
||||
class CreatureWeaponAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
||||
{
|
||||
public:
|
||||
CreatureWeaponAnimation(const MWWorld::Ptr& ptr);
|
||||
|
@ -36,11 +37,29 @@ namespace MWRender
|
|||
|
||||
void updatePart(NifOgre::ObjectScenePtr& scene, int slot);
|
||||
|
||||
virtual void attachArrow();
|
||||
virtual void releaseArrow();
|
||||
|
||||
virtual Ogre::Vector3 runAnimation(float duration);
|
||||
|
||||
/// A relative factor (0-1) that decides if and how much the skeleton should be pitched
|
||||
/// to indicate the facing orientation of the character.
|
||||
virtual void setPitchFactor(float factor) { mPitchFactor = factor; }
|
||||
|
||||
virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); }
|
||||
|
||||
// WeaponAnimation
|
||||
virtual NifOgre::ObjectScenePtr getWeapon() { return mWeapon; }
|
||||
virtual void showWeapon(bool show) { showWeapons(show); }
|
||||
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
|
||||
|
||||
private:
|
||||
NifOgre::ObjectScenePtr mWeapon;
|
||||
NifOgre::ObjectScenePtr mShield;
|
||||
bool mShowWeapons;
|
||||
bool mShowCarriedLeft;
|
||||
|
||||
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -76,27 +76,6 @@ float HeadAnimationTime::getValue() const
|
|||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static NpcAnimation::PartBoneMap createPartListMap()
|
||||
{
|
||||
NpcAnimation::PartBoneMap result;
|
||||
|
@ -147,8 +126,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
|
|||
mShowCarriedLeft(true),
|
||||
mFirstPersonOffset(0.f, 0.f, 0.f),
|
||||
mAlpha(1.f),
|
||||
mNpcType(Type_Normal),
|
||||
mPitchFactor(0)
|
||||
mNpcType(Type_Normal)
|
||||
{
|
||||
mNpc = mPtr.get<ESM::NPC>()->mBase;
|
||||
|
||||
|
@ -538,14 +516,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
|||
// updateSkeletonInstance, below, touches the hands.
|
||||
node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD);
|
||||
}
|
||||
else if (mPitchFactor > 0)
|
||||
else
|
||||
{
|
||||
// In third person mode we may still need pitch for ranged weapon targeting
|
||||
float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor;
|
||||
Ogre::Node *node = baseinst->getBone("Bip01 Spine2");
|
||||
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
|
||||
node = baseinst->getBone("Bip01 Spine1");
|
||||
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
|
||||
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst);
|
||||
}
|
||||
mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame.
|
||||
|
||||
|
@ -695,13 +669,14 @@ void NpcAnimation::showWeapons(bool showWeapon)
|
|||
{
|
||||
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if(weapon != inv.end()) // special case for weapons
|
||||
if(weapon != inv.end())
|
||||
{
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(*weapon);
|
||||
std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon);
|
||||
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,
|
||||
mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
|
||||
|
||||
// Crossbows start out with a bolt attached
|
||||
if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
|
||||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||
{
|
||||
|
@ -743,113 +718,24 @@ void NpcAnimation::showCarriedLeft(bool show)
|
|||
removeIndividualPart(ESM::PRT_Shield);
|
||||
}
|
||||
|
||||
void NpcAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
|
||||
{
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
|
||||
setRenderProperties(object, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0,
|
||||
!ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
|
||||
|
||||
std::for_each(object->mEntities.begin(), object->mEntities.end(), SetObjectGroup(slot));
|
||||
std::for_each(object->mParticles.begin(), object->mParticles.end(), SetObjectGroup(slot));
|
||||
}
|
||||
|
||||
void NpcAnimation::attachArrow()
|
||||
{
|
||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (weaponSlot != inv.end() && weaponSlot->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
|
||||
showWeapons(true);
|
||||
else
|
||||
{
|
||||
NifOgre::ObjectScenePtr weapon = mObjectParts[ESM::PRT_Weapon];
|
||||
|
||||
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", mInsert, model);
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(*ammo);
|
||||
setRenderProperties(mAmmunition, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0,
|
||||
!ammo->getClass().getEnchantment(*ammo).empty(), &glowColor);
|
||||
|
||||
std::for_each(mAmmunition->mEntities.begin(), mAmmunition->mEntities.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition));
|
||||
std::for_each(mAmmunition->mParticles.begin(), mAmmunition->mParticles.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition));
|
||||
}
|
||||
WeaponAnimation::attachArrow(mPtr);
|
||||
}
|
||||
|
||||
void NpcAnimation::releaseArrow()
|
||||
{
|
||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
||||
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(mPtr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
// 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 = mPtr.getClass().getCreatureStats(mPtr);
|
||||
MWMechanics::DynamicStat<float> fatigue = attackerStats.getFatigue();
|
||||
const float normalizedEncumbrance = mPtr.getClass().getEncumbrance(mPtr) / mPtr.getClass().getCapacity(mPtr);
|
||||
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<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
|
||||
{
|
||||
// Thrown weapons get detached now
|
||||
NifOgre::ObjectScenePtr objects = mObjectParts[ESM::PRT_Weapon];
|
||||
|
||||
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) *
|
||||
mPtr.getClass().getCreatureStats(mPtr).getAttackStrength();
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *weapon, launchPos, orient, *weapon, speed);
|
||||
|
||||
showWeapons(false);
|
||||
|
||||
inv.remove(*weapon, 1, mPtr);
|
||||
}
|
||||
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) * mPtr.getClass().getCreatureStats(mPtr).getAttackStrength();
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *ammo, launchPos, orient, *weapon, speed);
|
||||
|
||||
inv.remove(*ammo, 1, mPtr);
|
||||
mAmmunition.setNull();
|
||||
}
|
||||
WeaponAnimation::releaseArrow(mPtr);
|
||||
}
|
||||
|
||||
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "weaponanimation.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct NPC;
|
||||
|
@ -25,24 +27,7 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Animation* mAnimation;
|
||||
std::string mWeaponGroup;
|
||||
float mStartTime;
|
||||
public:
|
||||
WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {}
|
||||
void setGroup(const std::string& group);
|
||||
void updateStartTime();
|
||||
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener
|
||||
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
||||
{
|
||||
public:
|
||||
virtual void equipmentChanged() { updateParts(); }
|
||||
|
@ -91,7 +76,6 @@ private:
|
|||
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
|
||||
|
||||
float mAlpha;
|
||||
float mPitchFactor;
|
||||
|
||||
void updateNpcBase();
|
||||
|
||||
|
@ -138,7 +122,10 @@ public:
|
|||
virtual void attachArrow();
|
||||
virtual void releaseArrow();
|
||||
|
||||
NifOgre::ObjectScenePtr mAmmunition;
|
||||
// WeaponAnimation
|
||||
virtual NifOgre::ObjectScenePtr getWeapon() { return mObjectParts[ESM::PRT_Weapon]; }
|
||||
virtual void showWeapon(bool show) { showWeapons(show); }
|
||||
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
|
||||
|
||||
void setViewMode(ViewMode viewMode);
|
||||
|
||||
|
|
159
apps/openmw/mwrender/weaponanimation.cpp
Normal file
159
apps/openmw/mwrender/weaponanimation.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
#include "weaponanimation.hpp"
|
||||
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreBone.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreSkeletonInstance.h>
|
||||
|
||||
#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<ESM::Weapon>()->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<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
// 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<float> 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<ESM::Weapon>()->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 = skel->getBone("Bip01 Spine2");
|
||||
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
|
||||
node = skel->getBone("Bip01 Spine1");
|
||||
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
|
||||
}
|
||||
|
||||
}
|
56
apps/openmw/mwrender/weaponanimation.hpp
Normal file
56
apps/openmw/mwrender/weaponanimation.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef OPENMW_MWRENDER_WEAPONANIMATION_H
|
||||
#define OPENMW_MWRENDER_WEAPONANIMATION_H
|
||||
|
||||
#include <OgreController.h>
|
||||
|
||||
#include <components/nifogre/ogrenifloader.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class Animation;
|
||||
|
||||
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Animation* mAnimation;
|
||||
std::string mWeaponGroup;
|
||||
float mStartTime;
|
||||
public:
|
||||
WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {}
|
||||
void setGroup(const std::string& group);
|
||||
void updateStartTime();
|
||||
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{ }
|
||||
};
|
||||
|
||||
/// Handles attach & release of projectiles for ranged weapons
|
||||
class WeaponAnimation
|
||||
{
|
||||
public:
|
||||
WeaponAnimation() : mPitchFactor(0) {}
|
||||
|
||||
virtual void attachArrow(MWWorld::Ptr actor);
|
||||
virtual void releaseArrow(MWWorld::Ptr actor);
|
||||
|
||||
protected:
|
||||
NifOgre::ObjectScenePtr mAmmunition;
|
||||
|
||||
virtual NifOgre::ObjectScenePtr getWeapon() = 0;
|
||||
virtual void showWeapon(bool show) = 0;
|
||||
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot) = 0;
|
||||
|
||||
/// A relative factor (0-1) that decides if and how much the skeleton should be pitched
|
||||
/// to indicate the facing orientation of the character, for ranged weapon aiming.
|
||||
float mPitchFactor;
|
||||
|
||||
void pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue