mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-04 09:15:33 +00:00
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
|
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||||
actors objects renderinginterface localmap occlusionquery water shadows
|
actors objects renderinginterface localmap occlusionquery water shadows
|
||||||
characterpreview globalmap videoplayer ripplesimulation refraction
|
characterpreview globalmap videoplayer ripplesimulation refraction
|
||||||
terrainstorage renderconst effectmanager
|
terrainstorage renderconst effectmanager weaponanimation
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwinput
|
add_openmw_dir (mwinput
|
||||||
|
|
|
@ -55,6 +55,8 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr)
|
||||||
|
|
||||||
updateParts();
|
updateParts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mWeaponAnimationTime = Ogre::SharedPtr<WeaponAnimationTime>(new WeaponAnimationTime(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureWeaponAnimation::showWeapons(bool showWeapon)
|
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,
|
setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0,
|
||||||
!item.getClass().getEnchantment(item).empty(), &glowColor);
|
!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)
|
if(scene->mSkelBase)
|
||||||
{
|
{
|
||||||
Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton();
|
Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton();
|
||||||
|
@ -133,15 +149,42 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
|
||||||
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
|
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());
|
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(scene->mControllers.begin());
|
||||||
for(;ctrl != scene->mControllers.end();ctrl++)
|
for(;ctrl != scene->mControllers.end();ctrl++)
|
||||||
{
|
{
|
||||||
if(ctrl->getSource().isNull())
|
if(ctrl->getSource().isNull())
|
||||||
|
{
|
||||||
|
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
|
||||||
|
ctrl->setSource(mWeaponAnimationTime);
|
||||||
|
else
|
||||||
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime()));
|
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
|
#define GAME_RENDER_CREATUREANIMATION_H
|
||||||
|
|
||||||
#include "animation.hpp"
|
#include "animation.hpp"
|
||||||
|
#include "weaponanimation.hpp"
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
|
@ -21,7 +22,7 @@ namespace MWRender
|
||||||
// For creatures with weapons and shields
|
// For creatures with weapons and shields
|
||||||
// Animation is already virtual anyway, so might as well make a separate class.
|
// 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.
|
// 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:
|
public:
|
||||||
CreatureWeaponAnimation(const MWWorld::Ptr& ptr);
|
CreatureWeaponAnimation(const MWWorld::Ptr& ptr);
|
||||||
|
@ -36,11 +37,29 @@ namespace MWRender
|
||||||
|
|
||||||
void updatePart(NifOgre::ObjectScenePtr& scene, int slot);
|
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:
|
private:
|
||||||
NifOgre::ObjectScenePtr mWeapon;
|
NifOgre::ObjectScenePtr mWeapon;
|
||||||
NifOgre::ObjectScenePtr mShield;
|
NifOgre::ObjectScenePtr mShield;
|
||||||
bool mShowWeapons;
|
bool mShowWeapons;
|
||||||
bool mShowCarriedLeft;
|
bool mShowCarriedLeft;
|
||||||
|
|
||||||
|
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,27 +76,6 @@ float HeadAnimationTime::getValue() const
|
||||||
return 1;
|
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()
|
static NpcAnimation::PartBoneMap createPartListMap()
|
||||||
{
|
{
|
||||||
NpcAnimation::PartBoneMap result;
|
NpcAnimation::PartBoneMap result;
|
||||||
|
@ -147,8 +126,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
|
||||||
mShowCarriedLeft(true),
|
mShowCarriedLeft(true),
|
||||||
mFirstPersonOffset(0.f, 0.f, 0.f),
|
mFirstPersonOffset(0.f, 0.f, 0.f),
|
||||||
mAlpha(1.f),
|
mAlpha(1.f),
|
||||||
mNpcType(Type_Normal),
|
mNpcType(Type_Normal)
|
||||||
mPitchFactor(0)
|
|
||||||
{
|
{
|
||||||
mNpc = mPtr.get<ESM::NPC>()->mBase;
|
mNpc = mPtr.get<ESM::NPC>()->mBase;
|
||||||
|
|
||||||
|
@ -538,14 +516,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
||||||
// updateSkeletonInstance, below, touches the hands.
|
// updateSkeletonInstance, below, touches the hands.
|
||||||
node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD);
|
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
|
// In third person mode we may still need pitch for ranged weapon targeting
|
||||||
float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor;
|
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame.
|
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::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
|
||||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
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);
|
Ogre::Vector3 glowColor = getEnchantmentColor(*weapon);
|
||||||
std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon);
|
std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon);
|
||||||
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,
|
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,
|
||||||
mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
|
mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
|
||||||
|
|
||||||
|
// Crossbows start out with a bolt attached
|
||||||
if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
|
if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
|
||||||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||||
{
|
{
|
||||||
|
@ -743,113 +718,24 @@ void NpcAnimation::showCarriedLeft(bool show)
|
||||||
removeIndividualPart(ESM::PRT_Shield);
|
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()
|
void NpcAnimation::attachArrow()
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
WeaponAnimation::attachArrow(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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpcAnimation::releaseArrow()
|
void NpcAnimation::releaseArrow()
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
WeaponAnimation::releaseArrow(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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)
|
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
|
#include "weaponanimation.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
struct NPC;
|
struct NPC;
|
||||||
|
@ -25,24 +27,7 @@ public:
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void equipmentChanged() { updateParts(); }
|
virtual void equipmentChanged() { updateParts(); }
|
||||||
|
@ -91,7 +76,6 @@ private:
|
||||||
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
|
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
|
||||||
|
|
||||||
float mAlpha;
|
float mAlpha;
|
||||||
float mPitchFactor;
|
|
||||||
|
|
||||||
void updateNpcBase();
|
void updateNpcBase();
|
||||||
|
|
||||||
|
@ -138,7 +122,10 @@ public:
|
||||||
virtual void attachArrow();
|
virtual void attachArrow();
|
||||||
virtual void releaseArrow();
|
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);
|
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