1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-26 15:39:42 +00:00
openmw/apps/openmw/mwrender/weaponanimation.cpp
mrcheko 67abc60264 aiming to moving target in ranged combat ai
1) Taking into account target move vector and speed. However aiming is not
ideal, since attack strength can't be controlled directly. I did achieve
almost 100% accuracy updating it everyframe but then thought it would be
unfair, cause AI should mimic human targetting.
2) Also added in this commit func to measure real attack durations for
weapon.
2014-06-08 20:59:26 +04:00

159 lines
6.1 KiB
C++

#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, true);
}
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);
}
}