Fixed ranged combat for creatures

This commit is contained in:
scrawl 2014-03-12 11:30:44 +01:00
parent a9dcc90970
commit 0cd40294a2
7 changed files with 307 additions and 157 deletions

View file

@ -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

View file

@ -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;
}
}

View file

@ -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;
};
}

View file

@ -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)

View file

@ -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);

View 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);
}
}

View 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