You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
8.6 KiB
C++
275 lines
8.6 KiB
C++
#include "weaponanimation.hpp"
|
|
|
|
#include <osg/MatrixTransform>
|
|
|
|
#include <components/resource/resourcesystem.hpp>
|
|
#include <components/resource/scenemanager.hpp>
|
|
|
|
/*
|
|
Start of tes3mp addition
|
|
|
|
Include additional headers for multiplayer purposes
|
|
*/
|
|
#include "../mwmp/MechanicsHelper.hpp"
|
|
/*
|
|
End of tes3mp addition
|
|
*/
|
|
|
|
#include "../mwbase/world.hpp"
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/soundmanager.hpp"
|
|
|
|
#include "../mwworld/inventorystore.hpp"
|
|
#include "../mwworld/class.hpp"
|
|
#include "../mwworld/esmstore.hpp"
|
|
|
|
#include "../mwmechanics/creaturestats.hpp"
|
|
#include "../mwmechanics/combat.hpp"
|
|
|
|
#include "animation.hpp"
|
|
#include "rotatecontroller.hpp"
|
|
|
|
namespace MWRender
|
|
{
|
|
|
|
float WeaponAnimationTime::getValue(osg::NodeVisitor*)
|
|
{
|
|
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);
|
|
}
|
|
|
|
WeaponAnimation::WeaponAnimation()
|
|
: mPitchFactor(0)
|
|
{
|
|
}
|
|
|
|
WeaponAnimation::~WeaponAnimation()
|
|
{
|
|
|
|
}
|
|
|
|
void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
|
|
{
|
|
const MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
|
|
MWWorld::ConstContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
if (weaponSlot == inv.end())
|
|
return;
|
|
if (weaponSlot->getTypeName() != typeid(ESM::Weapon).name())
|
|
return;
|
|
int weaponType = weaponSlot->get<ESM::Weapon>()->mBase->mData.mType;
|
|
if (weaponType == ESM::Weapon::MarksmanThrown)
|
|
{
|
|
std::string soundid = weaponSlot->getClass().getUpSoundId(*weaponSlot);
|
|
if(!soundid.empty())
|
|
{
|
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
|
sndMgr->playSound3D(actor, soundid, 1.0f, 1.0f);
|
|
}
|
|
showWeapon(true);
|
|
}
|
|
else if (weaponType == ESM::Weapon::MarksmanBow || weaponType == ESM::Weapon::MarksmanCrossbow)
|
|
{
|
|
osg::Group* parent = getArrowBone();
|
|
if (!parent)
|
|
return;
|
|
|
|
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
|
if (ammo == inv.end())
|
|
return;
|
|
std::string model = ammo->getClass().getModel(*ammo);
|
|
|
|
osg::ref_ptr<osg::Node> arrow = getResourceSystem()->getSceneManager()->getInstance(model, parent);
|
|
|
|
mAmmunition = PartHolderPtr(new PartHolder(arrow));
|
|
}
|
|
}
|
|
|
|
void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
|
|
{
|
|
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
|
|
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
if (weapon == inv.end())
|
|
return;
|
|
if (weapon->getTypeName() != typeid(ESM::Weapon).name())
|
|
return;
|
|
|
|
/*
|
|
Start of tes3mp addition
|
|
|
|
If this is an attack by a LocalPlayer or LocalActor, record its attackStrength and
|
|
rangedWeaponId and prepare an attack packet for sending.
|
|
|
|
Unlike melee attacks, ranged attacks require the weapon and ammo IDs to be recorded
|
|
because players and actors can have multiple projectiles in the air at the same time.
|
|
|
|
If it's an attack by a DedicatedPlayer or DedicatedActor, apply the attackStrength
|
|
from their latest attack packet.
|
|
*/
|
|
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);
|
|
|
|
if (localAttack)
|
|
{
|
|
localAttack->attackStrength = attackStrength;
|
|
localAttack->rangedWeaponId = weapon->getCellRef().getRefId();
|
|
localAttack->shouldSend = true;
|
|
}
|
|
else
|
|
{
|
|
mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(actor);
|
|
|
|
if (dedicatedAttack)
|
|
attackStrength = dedicatedAttack->attackStrength;
|
|
}
|
|
/*
|
|
End of tes3mp addition
|
|
*/
|
|
|
|
// The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
|
|
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
|
|
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
|
|
|
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
|
|
|
MWMechanics::applyFatigueLoss(actor, *weapon, attackStrength);
|
|
|
|
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
|
|
{
|
|
/*
|
|
Start of tes3mp addition
|
|
|
|
If this is a local attack, clear the rangedAmmoId used for it
|
|
*/
|
|
if (localAttack)
|
|
localAttack->rangedAmmoId = "";
|
|
/*
|
|
End of tes3mp addition
|
|
*/
|
|
|
|
// Thrown weapons get detached now
|
|
osg::Node* weaponNode = getWeaponNode();
|
|
if (!weaponNode)
|
|
return;
|
|
osg::NodePathList nodepaths = weaponNode->getParentalNodePaths();
|
|
if (nodepaths.empty())
|
|
return;
|
|
osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
|
|
|
|
float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat();
|
|
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
|
|
float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength;
|
|
|
|
MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed, attackStrength);
|
|
|
|
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;
|
|
|
|
if (!mAmmunition)
|
|
return;
|
|
|
|
/*
|
|
Start of tes3mp addition
|
|
|
|
If this is a local attack, record the rangedAmmoId used for it
|
|
*/
|
|
if (localAttack)
|
|
localAttack->rangedAmmoId = ammo->getCellRef().getRefId();
|
|
/*
|
|
End of tes3mp addition
|
|
*/
|
|
|
|
osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode();
|
|
osg::NodePathList nodepaths = ammoNode->getParentalNodePaths();
|
|
if (nodepaths.empty())
|
|
return;
|
|
osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
|
|
|
|
float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat();
|
|
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
|
|
float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength;
|
|
|
|
MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed, attackStrength);
|
|
|
|
inv.remove(*ammo, 1, actor);
|
|
mAmmunition.reset();
|
|
}
|
|
}
|
|
|
|
void WeaponAnimation::addControllers(const std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >& nodes,
|
|
std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> > &map, osg::Node* objectRoot)
|
|
{
|
|
for (int i=0; i<2; ++i)
|
|
{
|
|
mSpineControllers[i] = NULL;
|
|
|
|
std::map<std::string, osg::ref_ptr<osg::MatrixTransform> >::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2");
|
|
if (found != nodes.end())
|
|
{
|
|
osg::Node* node = found->second;
|
|
mSpineControllers[i] = new RotateController(objectRoot);
|
|
node->addUpdateCallback(mSpineControllers[i]);
|
|
map.insert(std::make_pair(node, mSpineControllers[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
void WeaponAnimation::deleteControllers()
|
|
{
|
|
for (int i=0; i<2; ++i)
|
|
mSpineControllers[i] = NULL;
|
|
}
|
|
|
|
void WeaponAnimation::configureControllers(float characterPitchRadians)
|
|
{
|
|
if (mPitchFactor == 0.f || characterPitchRadians == 0.f)
|
|
{
|
|
setControllerEnabled(false);
|
|
return;
|
|
}
|
|
|
|
float pitch = characterPitchRadians * mPitchFactor;
|
|
osg::Quat rotate (pitch/2, osg::Vec3f(-1,0,0));
|
|
setControllerRotate(rotate);
|
|
setControllerEnabled(true);
|
|
}
|
|
|
|
void WeaponAnimation::setControllerRotate(const osg::Quat& rotate)
|
|
{
|
|
for (int i=0; i<2; ++i)
|
|
if (mSpineControllers[i])
|
|
mSpineControllers[i]->setRotate(rotate);
|
|
}
|
|
|
|
void WeaponAnimation::setControllerEnabled(bool enabled)
|
|
{
|
|
for (int i=0; i<2; ++i)
|
|
if (mSpineControllers[i])
|
|
mSpineControllers[i]->setEnabled(enabled);
|
|
}
|
|
|
|
}
|