Merge pull request #2413 from akortunov/weapon

Refactor weapon types behaviour
pull/541/head
Alexei Dobrohotov 5 years ago committed by GitHub
commit 307e9ba666
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -83,7 +83,7 @@ add_openmw_dir (mwmechanics
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
aicast aiescort aiface aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
character actors objects aistate coordinateconverter trading weaponpriority spellpriority
character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype
)
add_openmw_dir (mwstate

@ -20,6 +20,7 @@
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "../mwgui/tooltips.hpp"
@ -332,21 +333,13 @@ namespace MWClass
if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft)
{
MWWorld::ConstContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == invStore.end())
return std::make_pair(1,"");
if(weapon->getTypeName() == typeid(ESM::Weapon).name() &&
(weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoClose ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoWide ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::SpearTwoWide ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::AxeTwoHand ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanBow ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow))
if(weapon != invStore.end() && weapon->getTypeName() == typeid(ESM::Weapon).name())
{
return std::make_pair(3,"");
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
if (MWMechanics::getWeaponType(ref->mBase->mData.mType)->mFlags & ESM::WeaponType::TwoHanded)
return std::make_pair(3,"");
}
return std::make_pair(1,"");
}
}

@ -26,7 +26,7 @@
#include "../mwmechanics/combat.hpp"
#include "../mwmechanics/autocalcspell.hpp"
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwmechanics/character.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwworld/ptr.hpp"
@ -1228,9 +1228,9 @@ namespace MWClass
if (getNpcStats(ptr).isWerewolf()
&& getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
{
MWMechanics::WeaponType weaponType = MWMechanics::WeapType_None;
MWMechanics::getActiveWeapon(getCreatureStats(ptr), getInventoryStore(ptr), &weaponType);
if (weaponType == MWMechanics::WeapType_None)
int weaponType = ESM::Weapon::None;
MWMechanics::getActiveWeapon(ptr, &weaponType);
if (weaponType == ESM::Weapon::None)
return std::string();
}

@ -17,6 +17,8 @@
#include "../mwphysics/physicssystem.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwrender/objects.hpp"
@ -64,8 +66,9 @@ namespace MWClass
bool Weapon::hasItemHealth (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
int type = ref->mBase->mData.mType;
return (ref->mBase->mData.mType < ESM::Weapon::MarksmanThrown); // thrown weapons and arrows/bolts don't have health, only quantity
return MWMechanics::getWeaponType(type)->mFlags & ESM::WeaponType::HasHealth;
}
int Weapon::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const
@ -86,16 +89,17 @@ namespace MWClass
std::pair<std::vector<int>, bool> Weapon::getEquipmentSlots (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
ESM::WeaponType::Class weapClass = MWMechanics::getWeaponType(ref->mBase->mData.mType)->mWeaponClass;
std::vector<int> slots_;
bool stack = false;
if (ref->mBase->mData.mType==ESM::Weapon::Arrow || ref->mBase->mData.mType==ESM::Weapon::Bolt)
if (weapClass == ESM::WeaponType::Ammo)
{
slots_.push_back (int (MWWorld::InventoryStore::Slot_Ammunition));
stack = true;
}
else if (ref->mBase->mData.mType==ESM::Weapon::MarksmanThrown)
else if (weapClass == ESM::WeaponType::Thrown)
{
slots_.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));
stack = true;
@ -109,30 +113,9 @@ namespace MWClass
int Weapon::getEquipmentSkill (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
int type = ref->mBase->mData.mType;
const int size = 12;
static const int sMapping[size][2] =
{
{ ESM::Weapon::ShortBladeOneHand, ESM::Skill::ShortBlade },
{ ESM::Weapon::LongBladeOneHand, ESM::Skill::LongBlade },
{ ESM::Weapon::LongBladeTwoHand, ESM::Skill::LongBlade },
{ ESM::Weapon::BluntOneHand, ESM::Skill::BluntWeapon },
{ ESM::Weapon::BluntTwoClose, ESM::Skill::BluntWeapon },
{ ESM::Weapon::BluntTwoWide, ESM::Skill::BluntWeapon },
{ ESM::Weapon::SpearTwoWide, ESM::Skill::Spear },
{ ESM::Weapon::AxeOneHand, ESM::Skill::Axe },
{ ESM::Weapon::AxeTwoHand, ESM::Skill::Axe },
{ ESM::Weapon::MarksmanBow, ESM::Skill::Marksman },
{ ESM::Weapon::MarksmanCrossbow, ESM::Skill::Marksman },
{ ESM::Weapon::MarksmanThrown, ESM::Skill::Marksman }
};
for (int i=0; i<size; ++i)
if (sMapping[i][0]==ref->mBase->mData.mType)
return sMapping[i][1];
return -1;
return MWMechanics::getWeaponType(type)->mSkill;
}
int Weapon::getValue (const MWWorld::ConstPtr& ptr) const
@ -152,89 +135,17 @@ namespace MWClass
std::string Weapon::getUpSoundId (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
int type = ref->mBase->mData.mType;
// Ammo
if (type == 12 || type == 13)
{
return std::string("Item Ammo Up");
}
// Bow
if (type == 9)
{
return std::string("Item Weapon Bow Up");
}
// Crossbow
if (type == 10)
{
return std::string("Item Weapon Crossbow Up");
}
// Longblades, One hand and Two
if (type == 1 || type == 2)
{
return std::string("Item Weapon Longblade Up");
}
// Shortblade
if (type == 0)
{
return std::string("Item Weapon Shortblade Up");
}
// Spear
if (type == 6)
{
return std::string("Item Weapon Spear Up");
}
// Blunts, Axes and Thrown weapons
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11)
{
return std::string("Item Weapon Blunt Up");
}
return std::string("Item Misc Up");
std::string soundId = MWMechanics::getWeaponType(type)->mSoundId;
return soundId + " Up";
}
std::string Weapon::getDownSoundId (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
int type = ref->mBase->mData.mType;
// Ammo
if (type == 12 || type == 13)
{
return std::string("Item Ammo Down");
}
// Bow
if (type == 9)
{
return std::string("Item Weapon Bow Down");
}
// Crossbow
if (type == 10)
{
return std::string("Item Weapon Crossbow Down");
}
// Longblades, One hand and Two
if (type == 1 || type == 2)
{
return std::string("Item Weapon Longblade Down");
}
// Shortblade
if (type == 0)
{
return std::string("Item Weapon Shortblade Down");
}
// Spear
if (type == 6)
{
return std::string("Item Weapon Spear Down");
}
// Blunts, Axes and Thrown weapons
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11)
{
return std::string("Item Weapon Blunt Down");
}
return std::string("Item Misc Down");
std::string soundId = MWMechanics::getWeaponType(type)->mSoundId;
return soundId + " Down";
}
std::string Weapon::getInventoryIcon (const MWWorld::ConstPtr& ptr) const
@ -254,6 +165,7 @@ namespace MWClass
MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType);
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count);
@ -264,37 +176,26 @@ namespace MWClass
std::string text;
// weapon type & damage
if ((ref->mBase->mData.mType < ESM::Weapon::Arrow || Settings::Manager::getBool("show projectile damage", "Game")) && ref->mBase->mData.mType <= ESM::Weapon::Bolt)
if (weaponType->mWeaponClass != ESM::WeaponType::Ammo || Settings::Manager::getBool("show projectile damage", "Game"))
{
text += "\n#{sType} ";
static std::map <int, std::pair <std::string, std::string> > mapping;
if (mapping.empty())
int skill = MWMechanics::getWeaponType(ref->mBase->mData.mType)->mSkill;
const std::string type = ESM::Skill::sSkillNameIds[skill];
std::string oneOrTwoHanded;
if (weaponType->mWeaponClass == ESM::WeaponType::Melee)
{
mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded");
mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded");
mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded");
mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded");
mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded");
mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded");
mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded");
mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", "");
if (weaponType->mFlags & ESM::WeaponType::TwoHanded)
oneOrTwoHanded = "sTwoHanded";
else
oneOrTwoHanded = "sOneHanded";
}
const std::string type = mapping[ref->mBase->mData.mType].first;
const std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second;
text += store.get<ESM::GameSetting>().find(type)->mValue.getString() +
((oneOrTwoHanded != "") ? ", " + store.get<ESM::GameSetting>().find(oneOrTwoHanded)->mValue.getString() : "");
// weapon damage
if (ref->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
if (weaponType->mWeaponClass == ESM::WeaponType::Thrown)
{
// Thrown weapons have 2x real damage applied
// as they're both the weapon and the ammo
@ -302,14 +203,7 @@ namespace MWClass
+ MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[0] * 2))
+ " - " + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[1] * 2));
}
else if (ref->mBase->mData.mType >= ESM::Weapon::MarksmanBow)
{
// marksman
text += "\n#{sAttack}: "
+ MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[0]))
+ " - " + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[1]));
}
else
else if (weaponType->mWeaponClass == ESM::WeaponType::Melee)
{
// Chop
text += "\n#{sChop}: "
@ -324,6 +218,13 @@ namespace MWClass
+ MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mThrust[0]))
+ " - " + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mThrust[1]));
}
else
{
// marksman
text += "\n#{sAttack}: "
+ MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[0]))
+ " - " + MWGui::ToolTips::toString(static_cast<int>(ref->mBase->mData.mChop[1]));
}
}
if (hasItemHealth(ptr))
@ -335,7 +236,7 @@ namespace MWClass
const bool verbose = Settings::Manager::getBool("show melee info", "Game");
// add reach for melee weapon
if (ref->mBase->mData.mType < ESM::Weapon::MarksmanBow && verbose)
if (weaponType->mWeaponClass == ESM::WeaponType::Melee && verbose)
{
// display value in feet
const float combatDistance = store.get<ESM::GameSetting>().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach;
@ -344,7 +245,7 @@ namespace MWClass
}
// add attack speed for any weapon excepts arrows and bolts
if (ref->mBase->mData.mType < ESM::Weapon::Arrow && verbose)
if (weaponType->mWeaponClass != ESM::WeaponType::Ammo && verbose)
{
text += MWGui::ToolTips::getPercentString(ref->mBase->mData.mSpeed, "#{sAttributeSpeed}");
}
@ -403,13 +304,8 @@ namespace MWClass
if (slots_.first.empty())
return std::make_pair (0, "");
if(ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand ||
ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoClose ||
ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoWide ||
ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::SpearTwoWide ||
ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::AxeTwoHand ||
ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanBow ||
ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
int type = ptr.get<ESM::Weapon>()->mBase->mData.mType;
if(MWMechanics::getWeaponType(type)->mFlags & ESM::WeaponType::TwoHanded)
{
return std::make_pair (2, "");
}

@ -445,10 +445,9 @@ namespace MWMechanics
if (targetClass.hasInventoryStore(target))
{
MWMechanics::WeaponType weapType = WeapType_None;
MWWorld::ContainerStoreIterator weaponSlot =
MWMechanics::getActiveWeapon(targetClass.getCreatureStats(target), targetClass.getInventoryStore(target), &weapType);
if (weapType != WeapType_PickProbe && weapType != WeapType_Spell && weapType != WeapType_None && weapType != WeapType_HandToHand)
int weapType = ESM::Weapon::None;
MWWorld::ContainerStoreIterator weaponSlot = MWMechanics::getActiveWeapon(target, &weapType);
if (weapType > ESM::Weapon::None)
targetWeapon = *weaponSlot;
}
@ -645,7 +644,7 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
// get projectile speed (depending on weapon type)
if (weapType == ESM::Weapon::MarksmanThrown)
if (MWMechanics::getWeaponType(weapType)->mWeaponClass == ESM::WeaponType::Thrown)
{
static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat();
static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat();

@ -18,6 +18,7 @@
#include "combat.hpp"
#include "weaponpriority.hpp"
#include "spellpriority.hpp"
#include "weapontype.hpp"
namespace MWMechanics
{
@ -125,8 +126,7 @@ namespace MWMechanics
}
const ESM::Weapon* weapon = mWeapon.get<ESM::Weapon>()->mBase;
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
if (MWMechanics::getWeaponType(weapon->mData.mType)->mWeaponClass != ESM::WeaponType::Melee)
{
isRanged = true;
return fProjectileMaxSpeed;
@ -194,11 +194,12 @@ namespace MWMechanics
if (rating > bestActionRating)
{
const ESM::Weapon* weapon = it->get<ESM::Weapon>()->mBase;
int ammotype = getWeaponType(weapon->mData.mType)->mAmmoType;
MWWorld::Ptr ammo;
if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
if (ammotype == ESM::Weapon::Arrow)
ammo = bestArrow;
else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow)
else if (ammotype == ESM::Weapon::Bolt)
ammo = bestBolt;
bestActionRating = rating;
@ -367,7 +368,7 @@ namespace MWMechanics
else if (!activeWeapon.isEmpty())
{
const ESM::Weapon* esmWeap = activeWeapon.get<ESM::Weapon>()->mBase;
if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
if (MWMechanics::getWeaponType(esmWeap->mData.mType)->mWeaponClass != ESM::WeaponType::Melee)
{
static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat();
dist = fTargetSpellMaxSpeed;

@ -198,33 +198,6 @@ public:
};
static const struct WeaponInfo {
WeaponType type;
const char shortgroup[16];
const char longgroup[16];
} sWeaponTypeList[] = {
{ WeapType_HandToHand, "hh", "handtohand" },
{ WeapType_OneHand, "1h", "weapononehand" },
{ WeapType_TwoHand, "2c", "weapontwohand" },
{ WeapType_TwoWide, "2w", "weapontwowide" },
{ WeapType_BowAndArrow, "1h", "bowandarrow" },
{ WeapType_Crossbow, "crossbow", "crossbow" },
{ WeapType_Thrown, "1h", "throwweapon" },
{ WeapType_PickProbe, "1h", "pickprobe" },
{ WeapType_Spell, "spell", "spellcast" },
};
static const WeaponInfo *sWeaponTypeListEnd = &sWeaponTypeList[sizeof(sWeaponTypeList)/sizeof(sWeaponTypeList[0])];
class FindWeaponType {
WeaponType type;
public:
FindWeaponType(WeaponType _type) : type(_type) { }
bool operator()(const WeaponInfo &weap) const
{ return weap.type == type; }
};
std::string CharacterController::chooseRandomGroup (const std::string& prefix, int* num) const
{
int numAnims=0;
@ -315,7 +288,7 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle)
{
mAnimation->disable(mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped;
if (mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell)
if (mWeaponType > ESM::Weapon::None)
mAnimation->showWeapons(true);
}
else if (mUpperBodyState > UpperCharState_Nothing && mUpperBodyState < UpperCharState_WeapEquiped)
@ -347,7 +320,7 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle)
idle = CharState_None;
}
void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force)
void CharacterController::refreshJumpAnims(const std::string& weapShortGroup, JumpingState jump, CharacterState& idle, bool force)
{
if (!force && jump == mJumpState && idle == CharState_None)
return;
@ -357,22 +330,17 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
if (jump != JumpState_None)
{
jumpAnimName = "jump";
if(weap != sWeaponTypeListEnd)
if(!weapShortGroup.empty())
{
jumpAnimName += weap->shortgroup;
jumpAnimName += weapShortGroup;
if(!mAnimation->hasAnimation(jumpAnimName))
{
jumpmask = MWRender::Animation::BlendMask_LowerBody;
jumpAnimName = "jump";
jumpAnimName = fallbackShortWeaponGroup("jump", &jumpmask);
// Since we apply movement only for lower body, do not reset idle animations.
// If we apply jump only for lower body, do not reset idle animations.
// For upper body there will be idle animation.
if (idle == CharState_None)
if (jumpmask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None)
idle = CharState_Idle;
// For crossbow animations use 1h ones as fallback
if (mWeaponType == WeapType_Crossbow)
jumpAnimName += "1h";
}
}
}
@ -446,7 +414,65 @@ void CharacterController::onClose()
}
}
void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force)
std::string CharacterController::getWeaponAnimation(int weaponType) const
{
std::string weaponGroup = getWeaponType(weaponType)->mLongGroup;
bool isRealWeapon = weaponType != ESM::Weapon::HandToHand && weaponType != ESM::Weapon::Spell && weaponType != ESM::Weapon::None;
if (isRealWeapon && !mAnimation->hasAnimation(weaponGroup))
{
static const std::string oneHandFallback = getWeaponType(ESM::Weapon::LongBladeOneHand)->mLongGroup;
static const std::string twoHandFallback = getWeaponType(ESM::Weapon::LongBladeTwoHand)->mLongGroup;
const ESM::WeaponType* weapInfo = getWeaponType(weaponType);
// For real two-handed melee weapons use 2h swords animations as fallback, otherwise use the 1h ones
if (weapInfo->mFlags & ESM::WeaponType::TwoHanded && weapInfo->mWeaponClass == ESM::WeaponType::Melee)
weaponGroup = twoHandFallback;
else if (isRealWeapon)
weaponGroup = oneHandFallback;
}
return weaponGroup;
}
std::string CharacterController::fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask)
{
bool isRealWeapon = mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None;
if (!isRealWeapon)
{
if (blendMask != nullptr)
*blendMask = MWRender::Animation::BlendMask_LowerBody;
return baseGroupName;
}
static const std::string oneHandFallback = getWeaponType(ESM::Weapon::LongBladeOneHand)->mShortGroup;
static const std::string twoHandFallback = getWeaponType(ESM::Weapon::LongBladeTwoHand)->mShortGroup;
std::string groupName = baseGroupName;
const ESM::WeaponType* weapInfo = getWeaponType(mWeaponType);
// For real two-handed melee weapons use 2h swords animations as fallback, otherwise use the 1h ones
if (isRealWeapon && weapInfo->mFlags & ESM::WeaponType::TwoHanded && weapInfo->mWeaponClass == ESM::WeaponType::Melee)
groupName += twoHandFallback;
else if (isRealWeapon)
groupName += oneHandFallback;
// Special case for crossbows - we shouls apply 1h animations a fallback only for lower body
if (mWeaponType == ESM::Weapon::MarksmanCrossbow && blendMask != nullptr)
*blendMask = MWRender::Animation::BlendMask_LowerBody;
if (!mAnimation->hasAnimation(groupName))
{
groupName = baseGroupName;
if (blendMask != nullptr)
*blendMask = MWRender::Animation::BlendMask_LowerBody;
}
return groupName;
}
void CharacterController::refreshMovementAnims(const std::string& weapShortGroup, CharacterState movement, CharacterState& idle, bool force)
{
if (movement == mMovementState && idle == mIdleState && !force)
return;
@ -460,15 +486,15 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
if(movestate != sMovementListEnd)
{
movementAnimName = movestate->groupname;
if(weap != sWeaponTypeListEnd)
if(!weapShortGroup.empty())
{
std::string::size_type swimpos = movementAnimName.find("swim");
if (swimpos == std::string::npos)
{
if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case
movementAnimName = weap->shortgroup + movementAnimName;
if (mWeaponType == ESM::Weapon::Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case
movementAnimName = weapShortGroup + movementAnimName;
else
movementAnimName += weap->shortgroup;
movementAnimName += weapShortGroup;
}
if(!mAnimation->hasAnimation(movementAnimName))
@ -476,15 +502,12 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
movementAnimName = movestate->groupname;
if (swimpos == std::string::npos)
{
movemask = MWRender::Animation::BlendMask_LowerBody;
// Since we apply movement only for lower body, do not reset idle animations.
movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
// If we apply movement only for lower body, do not reset idle animations.
// For upper body there will be idle animation.
if (idle == CharState_None)
if (movemask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None)
idle = CharState_Idle;
// For crossbow animations use 1h ones as fallback
if (mWeaponType == WeapType_Crossbow)
movementAnimName += "1h";
}
else if (idle == CharState_None)
{
@ -520,18 +543,14 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
}
else
{
// For crossbow animations use 1h ones as fallback
if (mWeaponType == WeapType_Crossbow)
movementAnimName += "1h";
movementAnimName.erase(swimpos, 4);
if (weap != sWeaponTypeListEnd)
if (!weapShortGroup.empty())
{
std::string weapMovementAnimName = movementAnimName + weap->shortgroup;
std::string weapMovementAnimName = movementAnimName + weapShortGroup;
if(mAnimation->hasAnimation(weapMovementAnimName))
movementAnimName = weapMovementAnimName;
else
movemask = MWRender::Animation::BlendMask_LowerBody;
movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
}
if (!mAnimation->hasAnimation(movementAnimName))
@ -598,7 +617,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
}
}
void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force)
void CharacterController::refreshIdleAnims(const std::string& weapShortGroup, CharacterState idle, bool force)
{
// FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
// the idle animation should be displayed
@ -630,11 +649,13 @@ void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterStat
else if(mIdleState != CharState_None)
{
idleGroup = "idle";
if(weap != sWeaponTypeListEnd)
if(!weapShortGroup.empty())
{
idleGroup += weap->shortgroup;
idleGroup += weapShortGroup;
if(!mAnimation->hasAnimation(idleGroup))
idleGroup = "idle";
{
idleGroup = fallbackShortWeaponGroup("idle");
}
// play until the Loop Stop key 2 to 5 times, then play until the Stop key
// this replicates original engine behavior for the "Idle1h" 1st-person animation
@ -669,9 +690,9 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
if (mPtr.getClass().isActor())
refreshHitRecoilAnims(idle);
const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType));
if (!mPtr.getClass().hasInventoryStore(mPtr))
weap = sWeaponTypeListEnd;
std::string weap;
if (mPtr.getClass().hasInventoryStore(mPtr))
weap = getWeaponType(mWeaponType)->mShortGroup;
refreshJumpAnims(weap, jump, idle, force);
refreshMovementAnims(weap, movement, idle, force);
@ -680,77 +701,6 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
refreshIdleAnims(weap, idle, force);
}
void getWeaponGroup(WeaponType weaptype, std::string &group)
{
const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype));
if(info != sWeaponTypeListEnd)
group = info->longgroup;
else
group.clear();
}
MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype)
{
if(stats.getDrawState() == DrawState_Spell)
{
*weaptype = WeapType_Spell;
return inv.end();
}
if(stats.getDrawState() == MWMechanics::DrawState_Weapon)
{
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end())
*weaptype = WeapType_HandToHand;
else
{
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
*weaptype = WeapType_PickProbe;
else if(type == typeid(ESM::Weapon).name())
{
MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;
switch(weaponType)
{
case ESM::Weapon::ShortBladeOneHand:
case ESM::Weapon::LongBladeOneHand:
case ESM::Weapon::BluntOneHand:
case ESM::Weapon::AxeOneHand:
case ESM::Weapon::Arrow:
case ESM::Weapon::Bolt:
*weaptype = WeapType_OneHand;
break;
case ESM::Weapon::LongBladeTwoHand:
case ESM::Weapon::BluntTwoClose:
case ESM::Weapon::AxeTwoHand:
*weaptype = WeapType_TwoHand;
break;
case ESM::Weapon::BluntTwoWide:
case ESM::Weapon::SpearTwoWide:
*weaptype = WeapType_TwoWide;
break;
case ESM::Weapon::MarksmanBow:
*weaptype = WeapType_BowAndArrow;
break;
case ESM::Weapon::MarksmanCrossbow:
*weaptype = WeapType_Crossbow;
break;
case ESM::Weapon::MarksmanThrown:
*weaptype = WeapType_Thrown;
break;
}
}
}
return weapon;
}
return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
}
void CharacterController::playDeath(float startpoint, CharacterState death)
{
// Make sure the character was swimming upon death for forward-compatibility
@ -881,7 +831,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mHitState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing)
, mJumpState(JumpState_None)
, mWeaponType(WeapType_None)
, mWeaponType(ESM::Weapon::None)
, mAttackStrength(0.f)
, mSkipAnim(false)
, mSecondsOfSwimming(0)
@ -905,19 +855,20 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
if (cls.hasInventoryStore(mPtr))
{
getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType);
if (mWeaponType != WeapType_None)
getActiveWeapon(mPtr, &mWeaponType);
if (mWeaponType != ESM::Weapon::None)
{
mUpperBodyState = UpperCharState_WeapEquiped;
getWeaponGroup(mWeaponType, mCurrentWeapon);
mCurrentWeapon = getWeaponAnimation(mWeaponType);
}
if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand)
if(mWeaponType != ESM::Weapon::None && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::HandToHand)
{
mAnimation->showWeapons(true);
// Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly,
// for other weapons they should use absolute time. Some mods rely on this behaviour (to rotate throwing projectiles, for example)
bool useRelativeDuration = mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Crossbow;
ESM::WeaponType::Class weaponClass = getWeaponType(mWeaponType)->mWeaponClass;
bool useRelativeDuration = weaponClass == ESM::WeaponType::Ranged;
mAnimation->setWeaponGroup(mCurrentWeapon, useRelativeDuration);
}
@ -1157,11 +1108,11 @@ bool CharacterController::updateCreatureState()
const MWWorld::Class &cls = mPtr.getClass();
CreatureStats &stats = cls.getCreatureStats(mPtr);
WeaponType weapType = WeapType_None;
int weapType = ESM::Weapon::None;
if(stats.getDrawState() == DrawState_Weapon)
weapType = WeapType_HandToHand;
weapType = ESM::Weapon::HandToHand;
else if (stats.getDrawState() == DrawState_Spell)
weapType = WeapType_Spell;
weapType = ESM::Weapon::Spell;
if (weapType != mWeaponType)
{
@ -1178,7 +1129,7 @@ bool CharacterController::updateCreatureState()
std::string startKey = "start";
std::string stopKey = "stop";
if (weapType == WeapType_Spell)
if (weapType == ESM::Weapon::Spell)
{
const std::string spellid = stats.getSpells().getSelectedSpell();
bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);
@ -1214,7 +1165,7 @@ bool CharacterController::updateCreatureState()
mCurrentWeapon = "";
}
if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation
if (weapType != ESM::Weapon::Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation
{
mCurrentWeapon = chooseRandomAttackAnimation();
}
@ -1229,7 +1180,7 @@ bool CharacterController::updateCreatureState()
mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability());
if (weapType == WeapType_HandToHand)
if (weapType == ESM::Weapon::HandToHand)
playSwishSound(0.0f);
}
}
@ -1243,34 +1194,23 @@ bool CharacterController::updateCreatureState()
return false;
}
bool CharacterController::updateCarriedLeftVisible(WeaponType weaptype) const
bool CharacterController::updateCarriedLeftVisible(const int weaptype) const
{
// Shields/torches shouldn't be visible during any operation involving two hands
// There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop",
// but they are also present in weapon drawing animation.
switch (weaptype)
{
case WeapType_Spell:
case WeapType_BowAndArrow:
case WeapType_Crossbow:
case WeapType_HandToHand:
case WeapType_TwoHand:
case WeapType_TwoWide:
return false;
default:
return true;
}
return !(getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);
}
bool CharacterController::updateWeaponState(CharacterState& idle)
{
const MWWorld::Class &cls = mPtr.getClass();
CreatureStats &stats = cls.getCreatureStats(mPtr);
WeaponType weaptype = WeapType_None;
int weaptype = ESM::Weapon::None;
if(stats.getDrawState() == DrawState_Weapon)
weaptype = WeapType_HandToHand;
weaptype = ESM::Weapon::HandToHand;
else if (stats.getDrawState() == DrawState_Spell)
weaptype = WeapType_Spell;
weaptype = ESM::Weapon::Spell;
const bool isWerewolf = cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf();
@ -1280,18 +1220,18 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if (mPtr.getClass().hasInventoryStore(mPtr))
{
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype);
MWWorld::ContainerStoreIterator weapon = getActiveWeapon(mPtr, &weaptype);
if(stats.getDrawState() == DrawState_Spell)
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end() && mWeaponType != WeapType_HandToHand && weaptype > WeapType_HandToHand && weaptype < WeapType_Spell)
if(weapon != inv.end() && mWeaponType != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::Spell && weaptype != ESM::Weapon::None)
upSoundId = weapon->getClass().getUpSoundId(*weapon);
if(weapon != inv.end() && mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell)
if(weapon != inv.end() && mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None)
downSoundId = weapon->getClass().getDownSoundId(*weapon);
// weapon->HtH switch: weapon is empty already, so we need to take sound from previous weapon
if(weapon == inv.end() && !mWeapon.isEmpty() && weaptype == WeapType_HandToHand && mWeaponType != WeapType_Spell)
if(weapon == inv.end() && !mWeapon.isEmpty() && weaptype == ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell)
downSoundId = mWeapon.getClass().getDownSoundId(mWeapon);
MWWorld::Ptr newWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr();
@ -1312,8 +1252,8 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
bool forcestateupdate = false;
// We should not play equipping animation and sound during weapon->weapon transition
bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell &&
mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell;
bool isStillWeapon = weaptype != ESM::Weapon::HandToHand && weaptype != ESM::Weapon::Spell && weaptype != ESM::Weapon::None &&
mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None;
// If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires),
// we should force actor to the "weapon equipped" state, interrupt attack and update animations.
@ -1331,7 +1271,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if(!isKnockedOut() && !isKnockedDown() && !isRecovery())
{
std::string weapgroup;
if ((!isWerewolf || mWeaponType != WeapType_Spell)
if ((!isWerewolf || mWeaponType != ESM::Weapon::Spell)
&& weaptype != mWeaponType
&& mUpperBodyState != UpperCharState_UnEquipingWeap
&& !isStillWeapon)
@ -1340,7 +1280,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if (!weaponChanged)
{
// Note: we do not disable unequipping animation automatically to avoid body desync
getWeaponGroup(mWeaponType, weapgroup);
weapgroup = getWeaponAnimation(mWeaponType);
mAnimation->play(weapgroup, priorityWeapon,
MWRender::Animation::BlendMask_All, false,
1.0f, "unequip start", "unequip stop", 0.0f, 0);
@ -1368,17 +1308,18 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{
forcestateupdate = true;
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
weapgroup = getWeaponAnimation(weaptype);
getWeaponGroup(weaptype, weapgroup);
// Note: controllers for ranged weapon should use time for beginning of animation to play shooting properly,
// for other weapons they should use absolute time. Some mods rely on this behaviour (to rotate throwing projectiles, for example)
bool useRelativeDuration = weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow;
ESM::WeaponType::Class weaponClass = getWeaponType(weaptype)->mWeaponClass;
bool useRelativeDuration = weaponClass == ESM::WeaponType::Ranged;
mAnimation->setWeaponGroup(weapgroup, useRelativeDuration);
if (!isStillWeapon)
{
mAnimation->disable(mCurrentWeapon);
if (weaptype != WeapType_None)
if (weaptype != ESM::Weapon::None)
{
mAnimation->showWeapons(false);
mAnimation->play(weapgroup, priorityWeapon,
@ -1387,7 +1328,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
mUpperBodyState = UpperCharState_EquipingWeap;
// If we do not have the "equip attach" key, show weapon manually.
if (weaptype != WeapType_Spell)
if (weaptype != ESM::Weapon::Spell)
{
if (mAnimation->getTextKeyTime(weapgroup+": equip attach") < 0)
mAnimation->showWeapons(true);
@ -1407,7 +1348,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
}
mWeaponType = weaptype;
getWeaponGroup(mWeaponType, mCurrentWeapon);
mCurrentWeapon = getWeaponAnimation(mWeaponType);
if(!upSoundId.empty() && !isStillWeapon)
{
@ -1421,8 +1362,8 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{
mUpperBodyState = UpperCharState_Nothing;
mAnimation->disable(mCurrentWeapon);
mWeaponType = WeapType_None;
getWeaponGroup(mWeaponType, mCurrentWeapon);
mWeaponType = ESM::Weapon::None;
mCurrentWeapon = getWeaponAnimation(mWeaponType);
}
}
}
@ -1433,7 +1374,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if(cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run)
&& mHasMovedInXY
&& !MWBase::Environment::get().getWorld()->isSwimming(mPtr)
&& mWeaponType == WeapType_None)
&& mWeaponType == ESM::Weapon::None)
{
if(!sndMgr->getSoundPlaying(mPtr, "WolfRun"))
sndMgr->playSound3D(mPtr, "WolfRun", 1.0f, 1.0f, MWSound::Type::Sfx,
@ -1450,16 +1391,17 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if (mPtr.getClass().hasInventoryStore(mPtr))
{
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
MWWorld::ConstContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype);
MWWorld::ConstContainerStoreIterator weapon = getActiveWeapon(mPtr, &weaptype);
isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name());
if(isWeapon)
if (isWeapon)
{
weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
int ammotype = getWeaponType(weapon->get<ESM::Weapon>()->mBase->mData.mType)->mAmmoType;
if (ammotype != ESM::Weapon::None && (ammo == inv.end() || ammo->get<ESM::Weapon>()->mBase->mData.mType != ammotype))
ammunition = false;
}
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (mWeaponType == WeapType_Crossbow)
ammunition = (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt);
else if (mWeaponType == WeapType_BowAndArrow)
ammunition = (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Arrow);
if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped)
{
mAnimation->disable(mCurrentWeapon);
@ -1473,6 +1415,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
float complete;
bool animPlaying;
ESM::WeaponType::Class weapclass = getWeaponType(mWeaponType)->mWeaponClass;
if(mAttackingOrSpell)
{
MWWorld::Ptr player = getPlayer();
@ -1491,7 +1434,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
mCurrentWeapon = chooseRandomAttackAnimation();
}
if(mWeaponType == WeapType_Spell)
if(mWeaponType == ESM::Weapon::Spell)
{
// Unset casting flag, otherwise pressing the mouse button down would
// continue casting every frame if there is no animation
@ -1597,7 +1540,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
resetIdle = false;
}
}
else if(mWeaponType == WeapType_PickProbe)
else if(mWeaponType == ESM::Weapon::PickProbe)
{
MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
MWWorld::Ptr item = *weapon;
@ -1627,7 +1570,8 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{
std::string startKey;
std::string stopKey;
if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown)
if(weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)
{
mAttackType = "shoot";
startKey = mAttackType+" start";
@ -1699,7 +1643,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
attackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability());
}
if(mWeaponType != WeapType_Crossbow && mWeaponType != WeapType_BowAndArrow)
if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
@ -1731,7 +1675,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if (mUpperBodyState > UpperCharState_WeapEquiped)
{
mUpperBodyState = UpperCharState_WeapEquiped;
if (mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell)
if (mWeaponType > ESM::Weapon::None)
mAnimation->showWeapons(true);
}
mAnimation->disable(mCurrentWeapon);
@ -1739,9 +1683,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
}
mAnimation->setPitchFactor(0.f);
if (mWeaponType == WeapType_BowAndArrow ||
mWeaponType == WeapType_Thrown ||
mWeaponType == WeapType_Crossbow)
if (weapclass == ESM::WeaponType::Ranged || weapclass == ESM::WeaponType::Thrown)
{
switch (mUpperBodyState)
{
@ -1758,7 +1700,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{
// technically we do not need a pitch for crossbow reload animation,
// but we should avoid abrupt repositioning
if (mWeaponType == WeapType_Crossbow)
if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
mAnimation->setPitchFactor(std::max(0.f, 1.f-complete*10.f));
else
mAnimation->setPitchFactor(1.f-complete);
@ -1775,7 +1717,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
mUpperBodyState == UpperCharState_FollowStartToFollowStop ||
mUpperBodyState == UpperCharState_CastingSpell)
{
if (ammunition && mWeaponType == WeapType_Crossbow)
if (ammunition && mWeaponType == ESM::Weapon::MarksmanCrossbow)
mAnimation->attachArrow();
mUpperBodyState = UpperCharState_WeapEquiped;
@ -1852,7 +1794,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if(!start.empty())
{
int mask = MWRender::Animation::BlendMask_All;
if (mWeaponType == WeapType_Crossbow)
if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
mask = MWRender::Animation::BlendMask_UpperBody;
mAnimation->disable(mCurrentWeapon);
@ -2639,7 +2581,7 @@ void CharacterController::resurrect()
mAnimation->disable(mCurrentDeath);
mCurrentDeath.clear();
mDeathState = CharState_None;
mWeaponType = WeapType_None;
mWeaponType = ESM::Weapon::None;
}
void CharacterController::updateContinuousVfx()

@ -10,6 +10,8 @@
#include "../mwrender/animation.hpp"
#include "weapontype.hpp"
namespace MWWorld
{
class InventoryStore;
@ -113,21 +115,6 @@ enum CharacterState {
CharState_Block
};
enum WeaponType {
WeapType_None,
WeapType_HandToHand,
WeapType_OneHand,
WeapType_TwoHand,
WeapType_TwoWide,
WeapType_BowAndArrow,
WeapType_Crossbow,
WeapType_Thrown,
WeapType_PickProbe,
WeapType_Spell
};
enum UpperBodyCharacterState {
UpperCharState_Nothing,
UpperCharState_EquipingWeap,
@ -186,7 +173,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
JumpingState mJumpState;
std::string mCurrentJump;
WeaponType mWeaponType;
int mWeaponType;
std::string mCurrentWeapon;
float mAttackStrength;
@ -212,9 +199,9 @@ class CharacterController : public MWRender::Animation::TextKeyListener
void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);
void refreshHitRecoilAnims(CharacterState& idle);
void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force=false);
void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force=false);
void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false);
void refreshJumpAnims(const std::string& weapShortGroup, JumpingState jump, CharacterState& idle, bool force=false);
void refreshMovementAnims(const std::string& weapShortGroup, CharacterState movement, CharacterState& idle, bool force=false);
void refreshIdleAnims(const std::string& weapShortGroup, CharacterState idle, bool force=false);
void clearAnimQueue(bool clearPersistAnims = false);
@ -241,7 +228,11 @@ class CharacterController : public MWRender::Animation::TextKeyListener
/// @param num if non-nullptr, the chosen animation number will be written here
std::string chooseRandomGroup (const std::string& prefix, int* num = nullptr) const;
bool updateCarriedLeftVisible(WeaponType weaptype) const;
bool updateCarriedLeftVisible(int weaptype) const;
std::string fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask = nullptr);
std::string getWeaponAnimation(int weaponType) const;
public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
@ -312,8 +303,6 @@ public:
void playSwishSound(float attackStrength);
};
MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype);
}
#endif /* GAME_MWMECHANICS_CHARACTER_HPP */

@ -26,6 +26,7 @@
#include "npcstats.hpp"
#include "actorutil.hpp"
#include "aifollow.hpp"
#include "weapontype.hpp"
namespace MWMechanics
{
@ -826,8 +827,9 @@ namespace MWMechanics
bool isProjectile = false;
if (item.getTypeName() == typeid(ESM::Weapon).name())
{
const ESM::Weapon* ref = item.get<ESM::Weapon>()->mBase;
isProjectile = ref->mData.mType >= ESM::Weapon::MarksmanThrown;
int type = item.get<ESM::Weapon>()->mBase->mData.mType;
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;
isProjectile = (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ranged);
}
int type = enchantment->mData.mType;

@ -16,6 +16,8 @@
#include "creaturestats.hpp"
#include "spellcasting.hpp"
#include "weapontype.hpp"
#include "combat.hpp"
namespace
{
@ -376,7 +378,7 @@ namespace MWMechanics
case ESM::MagicEffect::BoundLongbow:
// AI should not summon the bow if there is no suitable ammo.
if (rateAmmo(actor, enemy, ESM::Weapon::Arrow) <= 0.f)
if (rateAmmo(actor, enemy, getWeaponType(ESM::Weapon::MarksmanBow)->mAmmoType) <= 0.f)
return 0.f;
break;

@ -14,6 +14,7 @@
#include "aicombataction.hpp"
#include "spellpriority.hpp"
#include "spellcasting.hpp"
#include "weapontype.hpp"
namespace MWMechanics
{
@ -34,14 +35,15 @@ namespace MWMechanics
const MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();
if (type == -1 && (weapon->mData.mType == ESM::Weapon::Arrow || weapon->mData.mType == ESM::Weapon::Bolt))
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(weapon->mData.mType)->mWeaponClass;
if (type == -1 && weapclass == ESM::WeaponType::Ammo)
return 0.f;
float rating=0.f;
static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat();
float ratingMult = fAIMeleeWeaponMult;
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown)
if (weapclass != ESM::WeaponType::Melee)
{
// Underwater ranged combat is impossible
if (world->isUnderwater(MWWorld::ConstPtr(actor), 0.75f)
@ -59,11 +61,11 @@ namespace MWMechanics
const float chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f;
// We need to account for the fact that thrown weapons have 2x real damage applied to the target
// as they're both the weapon and the ammo of the hit
if (weapon->mData.mType == ESM::Weapon::MarksmanThrown)
if (weapclass == ESM::WeaponType::Thrown)
{
rating = chop * 2;
}
else if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
else if (weapclass != ESM::WeaponType::Melee)
{
rating = chop;
}
@ -76,24 +78,28 @@ namespace MWMechanics
adjustWeaponDamage(rating, item, actor);
if (weapon->mData.mType != ESM::Weapon::MarksmanBow && weapon->mData.mType != ESM::Weapon::MarksmanCrossbow)
if (weapclass != ESM::WeaponType::Ranged)
{
resistNormalWeapon(enemy, actor, item, rating);
applyWerewolfDamageMult(enemy, item, rating);
}
else if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
{
if (arrowRating <= 0.f)
rating = 0.f;
else
rating += arrowRating;
}
else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow)
else
{
if (boltRating <= 0.f)
rating = 0.f;
else
rating += boltRating;
int ammotype = MWMechanics::getWeaponType(weapon->mData.mType)->mAmmoType;
if (ammotype == ESM::Weapon::Arrow)
{
if (arrowRating <= 0.f)
rating = 0.f;
else
rating += arrowRating;
}
else if (ammotype == ESM::Weapon::Bolt)
{
if (boltRating <= 0.f)
rating = 0.f;
else
rating += boltRating;
}
}
if (!weapon->mEnchant.empty())
@ -104,7 +110,7 @@ namespace MWMechanics
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
float charge = item.getCellRef().getEnchantmentCharge();
if (charge == -1 || charge >= castCost || weapon->mData.mType >= ESM::Weapon::MarksmanThrown)
if (charge == -1 || charge >= castCost || weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)
rating += rateEffects(enchantment->mEffects, actor, enemy);
}
}
@ -126,13 +132,13 @@ namespace MWMechanics
float chance = getHitChance(actor, enemy, value) / 100.f;
rating *= std::min(1.f, std::max(0.01f, chance));
if (weapon->mData.mType < ESM::Weapon::Arrow)
if (weapclass != ESM::WeaponType::Ammo)
rating *= weapon->mData.mSpeed;
return rating * ratingMult;
}
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType)
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, int ammoType)
{
float bestAmmoRating = 0.f;
if (!actor.getClass().hasInventoryStore(actor))
@ -153,7 +159,7 @@ namespace MWMechanics
return bestAmmoRating;
}
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, ESM::Weapon::Type ammoType)
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, int ammoType)
{
MWWorld::Ptr emptyPtr;
return rateAmmo(actor, enemy, emptyPtr, ammoType);
@ -175,8 +181,8 @@ namespace MWMechanics
float bonusDamage = 0.f;
const ESM::Weapon* esmWeap = weapon.get<ESM::Weapon>()->mBase;
if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
int type = esmWeap->mData.mType;
if (getWeaponType(type)->mWeaponClass != ESM::WeaponType::Melee)
{
if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy))
{

@ -10,8 +10,8 @@ namespace MWMechanics
float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy,
int type=-1, float arrowRating=0.f, float boltRating=0.f);
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType);
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, ESM::Weapon::Type ammoType);
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, int ammoType);
float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, int ammoType);
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
}

@ -0,0 +1,53 @@
#include "weapontype.hpp"
#include "../mwworld/class.hpp"
namespace MWMechanics
{
static const ESM::WeaponType *sWeaponTypeListEnd = &sWeaponTypeList[sizeof(sWeaponTypeList)/sizeof(sWeaponTypeList[0])];
MWWorld::ContainerStoreIterator getActiveWeapon(MWWorld::Ptr actor, int *weaptype)
{
MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor);
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
if(stats.getDrawState() == MWMechanics::DrawState_Spell)
{
*weaptype = ESM::Weapon::Spell;
return inv.end();
}
if(stats.getDrawState() == MWMechanics::DrawState_Weapon)
{
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end())
*weaptype = ESM::Weapon::HandToHand;
else
{
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Weapon).name())
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
*weaptype = ref->mBase->mData.mType;
}
else if (type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
*weaptype = ESM::Weapon::PickProbe;
}
return weapon;
}
return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
}
const ESM::WeaponType* getWeaponType(const int weaponType)
{
std::map<int, ESM::WeaponType>::const_iterator found = sWeaponTypeList.find(weaponType);
if (found == sWeaponTypeList.end())
{
// Use one-handed short blades as fallback
return &sWeaponTypeList[0];
}
return &found->second;
}
}

@ -0,0 +1,271 @@
#ifndef GAME_MWMECHANICS_WEAPONTYPE_H
#define GAME_MWMECHANICS_WEAPONTYPE_H
#include "../mwworld/inventorystore.hpp"
#include "creaturestats.hpp"
namespace MWMechanics
{
static std::map<int, ESM::WeaponType> sWeaponTypeList =
{
{
ESM::Weapon::None,
{
/* short group */ "",
/* long group */ "",
/* sound ID */ "",
/* attach bone */ "",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::HandToHand,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ 0
}
},
{
ESM::Weapon::PickProbe,
{
/* short group */ "1h",
/* long group */ "pickprobe",
/* sound ID */ "",
/* attach bone */ "",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::Security,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ 0
}
},
{
ESM::Weapon::Spell,
{
/* short group */ "spell",
/* long group */ "spellcast",
/* sound ID */ "",
/* attach bone */ "",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::HandToHand,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::HandToHand,
{
/* short group */ "hh",
/* long group */ "handtohand",
/* sound ID */ "",
/* attach bone */ "",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::HandToHand,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::ShortBladeOneHand,
{
/* short group */ "1s",
/* long group */ "shortbladeonehand",
/* sound ID */ "Item Weapon Shortblade",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 ShortBladeOneHand",
/* usage skill */ ESM::Skill::ShortBlade,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth
}
},
{
ESM::Weapon::LongBladeOneHand,
{
/* short group */ "1h",
/* long group */ "weapononehand",
/* sound ID */ "Item Weapon Longblade",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 LongBladeOneHand",
/* usage skill */ ESM::Skill::LongBlade,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth
}
},
{
ESM::Weapon::BluntOneHand,
{
/* short group */ "1b",
/* long group */ "bluntonehand",
/* sound ID */ "Item Weapon Blunt",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 BluntOneHand",
/* usage skill */ ESM::Skill::BluntWeapon,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth
}
},
{
ESM::Weapon::AxeOneHand,
{
/* short group */ "1b",
/* long group */ "bluntonehand",
/* sound ID */ "Item Weapon Blunt",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 LongBladeOneHand",
/* usage skill */ ESM::Skill::Axe,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth
}
},
{
ESM::Weapon::LongBladeTwoHand,
{
/* short group */ "2c",
/* long group */ "weapontwohand",
/* sound ID */ "Item Weapon Longblade",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 LongBladeTwoClose",
/* usage skill */ ESM::Skill::LongBlade,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::AxeTwoHand,
{
/* short group */ "2b",
/* long group */ "bluntwohand",
/* sound ID */ "Item Weapon Blunt",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 AxeTwoClose",
/* usage skill */ ESM::Skill::Axe,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::BluntTwoClose,
{
/* short group */ "2b",
/* long group */ "bluntwohand",
/* sound ID */ "Item Weapon Blunt",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 BluntTwoClose",
/* usage skill */ ESM::Skill::BluntWeapon,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::BluntTwoWide,
{
/* short group */ "2w",
/* long group */ "weapontwowide",
/* sound ID */ "Item Weapon Blunt",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 BluntTwoWide",
/* usage skill */ ESM::Skill::BluntWeapon,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::SpearTwoWide,
{
/* short group */ "2w",
/* long group */ "weapontwowide",
/* sound ID */ "Item Weapon Spear",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 SpearTwoWide",
/* usage skill */ ESM::Skill::Spear,
/* weapon class*/ ESM::WeaponType::Melee,
/* ammo type */ ESM::Weapon::None,
/* flags */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::MarksmanBow,
{
/* short group */ "bow",
/* long group */ "bowandarrow",
/* sound ID */ "Item Weapon Bow",
/* attach bone */ "Weapon Bone Left",
/* sheath bone */ "Bip01 MarksmanBow",
/* usage skill */ ESM::Skill::Marksman,
/* weapon class*/ ESM::WeaponType::Ranged,
/* ammo type */ ESM::Weapon::Arrow,
/* flags */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::MarksmanCrossbow,
{
/* short group */ "crossbow",
/* long group */ "crossbow",
/* sound ID */ "Item Weapon Crossbow",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 MarksmanCrossbow",
/* usage skill */ ESM::Skill::Marksman,
/* weapon class*/ ESM::WeaponType::Ranged,
/* ammo type */ ESM::Weapon::Bolt,
/* flags */ ESM::WeaponType::HasHealth|ESM::WeaponType::TwoHanded
}
},
{
ESM::Weapon::MarksmanThrown,
{
/* short group */ "1h",
/* long group */ "throwweapon",
/* sound ID */ "Item Weapon Blunt",
/* attach bone */ "Weapon Bone",
/* sheath bone */ "Bip01 MarksmanThrown",
/* usage skill */ ESM::Skill::Marksman,
/* weapon class*/ ESM::WeaponType::Thrown,
/* ammo type */ ESM::Weapon::None,
/* flags */ 0
}
},
{
ESM::Weapon::Arrow,
{
/* short group */ "",
/* long group */ "",
/* sound ID */ "Item Weapon Ammo",
/* attach bone */ "ArrowBone",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::Marksman,
/* weapon class*/ ESM::WeaponType::Ammo,
/* ammo type */ ESM::Weapon::None,
/* flags */ 0
}
},
{
ESM::Weapon::Bolt,
{
/* short group */ "",
/* long group */ "",
/* sound ID */ "Item Weapon Ammo",
/* attach bone */ "ArrowBone",
/* sheath bone */ "",
/* usage skill */ ESM::Skill::Marksman,
/* weapon class*/ ESM::WeaponType::Ammo,
/* ammo type */ ESM::Weapon::None,
/* flags */ 0
}
}
};
MWWorld::ContainerStoreIterator getActiveWeapon(MWWorld::Ptr actor, int *weaptype);
const ESM::WeaponType* getWeaponType(const int weaponType);
}
#endif

@ -28,6 +28,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "vismask.hpp"
@ -51,8 +52,6 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group>
// Make sure we cleaned object from effects, just in cast if we re-use node
removeEffects();
mWeaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game");
}
ActorAnimation::~ActorAnimation()
@ -84,7 +83,7 @@ PartHolderPtr ActorAnimation::getWeaponPart(const std::string& model, const std:
return PartHolderPtr(new PartHolder(instance));
}
osg::Group* ActorAnimation::getBoneByName(std::string boneName)
osg::Group* ActorAnimation::getBoneByName(const std::string& boneName)
{
if (!mObjectRoot)
return nullptr;
@ -105,93 +104,13 @@ std::string ActorAnimation::getHolsteredWeaponBoneName(const MWWorld::ConstPtr&
if(type == typeid(ESM::Weapon).name())
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon.get<ESM::Weapon>();
ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType;
return getHolsteredWeaponBoneName(weaponType);
}
return boneName;
}
std::string ActorAnimation::getHolsteredWeaponBoneName(const unsigned int weaponType)
{
std::string boneName;
switch(weaponType)
{
case ESM::Weapon::ShortBladeOneHand:
boneName = "Bip01 ShortBladeOneHand";
break;
case ESM::Weapon::LongBladeOneHand:
boneName = "Bip01 LongBladeOneHand";
break;
case ESM::Weapon::BluntOneHand:
boneName = "Bip01 BluntOneHand";
break;
case ESM::Weapon::AxeOneHand:
boneName = "Bip01 LongBladeOneHand";
break;
case ESM::Weapon::LongBladeTwoHand:
boneName = "Bip01 LongBladeTwoClose";
break;
case ESM::Weapon::BluntTwoClose:
boneName = "Bip01 BluntTwoClose";
break;
case ESM::Weapon::AxeTwoHand:
boneName = "Bip01 AxeTwoClose";
break;
case ESM::Weapon::BluntTwoWide:
boneName = "Bip01 BluntTwoWide";
break;
case ESM::Weapon::SpearTwoWide:
boneName = "Bip01 SpearTwoWide";
break;
case ESM::Weapon::MarksmanBow:
boneName = "Bip01 MarksmanBow";
break;
case ESM::Weapon::MarksmanCrossbow:
boneName = "Bip01 MarksmanCrossbow";
break;
case ESM::Weapon::MarksmanThrown:
boneName = "Bip01 MarksmanThrown";
break;
default:
break;
int weaponType = ref->mBase->mData.mType;
return MWMechanics::getWeaponType(weaponType)->mSheathingBone;
}
return boneName;
}
void ActorAnimation::injectWeaponBones()
{
if (!mResourceSystem->getVFS()->exists("meshes\\xbase_anim_sh.nif"))
{
mWeaponSheathing = false;
return;
}
osg::ref_ptr<osg::Node> sheathSkeleton = mResourceSystem->getSceneManager()->getInstance("meshes\\xbase_anim_sh.nif");
for (unsigned int type=0; type<=ESM::Weapon::MarksmanThrown; ++type)
{
const std::string holsteredBoneName = getHolsteredWeaponBoneName(type);
SceneUtil::FindByNameVisitor findVisitor (holsteredBoneName);
sheathSkeleton->accept(findVisitor);
osg::ref_ptr<osg::Node> sheathNode = findVisitor.mFoundNode;
if (sheathNode && sheathNode.get()->getNumParents())
{
osg::Group* sheathParent = getBoneByName(sheathNode.get()->getParent(0)->getName());
if (sheathParent)
{
sheathNode.get()->getParent(0)->removeChild(sheathNode);
sheathParent->addChild(sheathNode);
}
}
}
}
void ActorAnimation::resetControllers(osg::Node* node)
{
if (node == nullptr)
@ -205,7 +124,8 @@ void ActorAnimation::resetControllers(osg::Node* node)
void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
{
if (!mWeaponSheathing)
static const bool weaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game");
if (!weaponSheathing)
return;
if (!mPtr.getClass().hasInventoryStore(mPtr))
@ -219,7 +139,8 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
return;
// Since throwing weapons stack themselves, do not show such weapon itself
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
showHolsteredWeapons = false;
std::string mesh = weapon->getClass().getModel(*weapon);
@ -279,7 +200,8 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
void ActorAnimation::updateQuiver()
{
if (!mWeaponSheathing)
static const bool weaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game");
if (!weaponSheathing)
return;
if (!mPtr.getClass().hasInventoryStore(mPtr))
@ -303,10 +225,12 @@ void ActorAnimation::updateQuiver()
bool suitableAmmo = false;
MWWorld::ConstContainerStoreIterator ammo = weapon;
unsigned int ammoCount = 0;
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
const auto& weaponType = MWMechanics::getWeaponType(type);
if (weaponType->mWeaponClass == ESM::WeaponType::Thrown)
{
ammoCount = ammo->getRefData().getCount();
osg::Group* throwingWeaponNode = getBoneByName("Weapon Bone");
osg::Group* throwingWeaponNode = getBoneByName(weaponType->mAttachBone);
if (throwingWeaponNode && throwingWeaponNode->getNumChildren())
ammoCount--;
@ -323,10 +247,7 @@ void ActorAnimation::updateQuiver()
if (arrowAttached)
ammoCount--;
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
suitableAmmo = ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt;
else if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanBow)
suitableAmmo = ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Arrow;
suitableAmmo = ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponType->mAmmoType;
}
if (!suitableAmmo)
@ -382,7 +303,8 @@ void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/)
return;
MWWorld::ConstContainerStoreIterator ammo = inv.end();
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
ammo = weapon;
else
ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
@ -415,7 +337,8 @@ void ActorAnimation::itemRemoved(const MWWorld::ConstPtr& item, int /*count*/)
return;
MWWorld::ConstContainerStoreIterator ammo = inv.end();
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
if (MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown)
ammo = weapon;
else
ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);

@ -40,13 +40,10 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
virtual bool isArrowAttached() const { return false; }
protected:
bool mWeaponSheathing;
osg::Group* getBoneByName(std::string boneName);
osg::Group* getBoneByName(const std::string& boneName);
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
virtual void injectWeaponBones();
virtual void updateQuiver();
virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
virtual std::string getHolsteredWeaponBoneName(const unsigned int weaponType);
virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename)
{

@ -234,6 +234,25 @@ namespace
std::vector<std::pair<osg::Node*, osg::Group*> > mToRemove;
};
class GetExtendedBonesVisitor : public osg::NodeVisitor
{
public:
GetExtendedBonesVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
{
}
void apply(osg::Node& node)
{
if (SceneUtil::hasUserDescription(&node, "CustomBone"))
mFoundBones.emplace_back(&node, node.getParent(0));
traverse(node);
}
std::vector<std::pair<osg::Node*, osg::Group*> > mFoundBones;
};
class RemoveFinishedCallbackVisitor : public RemoveVisitor
{
public:
@ -1336,8 +1355,66 @@ namespace MWRender
state->second.mLoopingEnabled = enabled;
}
osg::ref_ptr<osg::Node> getModelInstance(Resource::SceneManager* sceneMgr, const std::string& model, bool baseonly)
void loadBonesFromFile(osg::ref_ptr<osg::Node>& baseNode, const std::string &model, Resource::ResourceSystem* resourceSystem)
{
const osg::Node* node = resourceSystem->getSceneManager()->getTemplate(model).get();
osg::ref_ptr<osg::Node> sheathSkeleton (const_cast<osg::Node*>(node)); // const-trickery required because there is no const version of NodeVisitor
GetExtendedBonesVisitor getBonesVisitor;
sheathSkeleton->accept(getBonesVisitor);
for (auto& nodePair : getBonesVisitor.mFoundBones)
{
SceneUtil::FindByNameVisitor findVisitor (nodePair.second->getName());
baseNode->accept(findVisitor);
osg::Group* sheathParent = findVisitor.mFoundNode;
if (sheathParent)
{
osg::Node* copy = osg::clone(nodePair.first, osg::CopyOp::DEEP_COPY_NODES);
sheathParent->addChild(copy);
}
}
}
void injectCustomBones(osg::ref_ptr<osg::Node>& node, const std::string& model, Resource::ResourceSystem* resourceSystem)
{
const std::map<std::string, VFS::File*>& index = resourceSystem->getVFS()->getIndex();
std::string animationPath = model;
if (animationPath.find("meshes") == 0)
{
animationPath.replace(0, 6, "animations");
}
animationPath.replace(animationPath.size()-4, 4, "/");
resourceSystem->getVFS()->normalizeFilename(animationPath);
std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(animationPath);
while (found != index.end())
{
const std::string& name = found->first;
if (name.size() >= animationPath.size() && name.substr(0, animationPath.size()) == animationPath)
{
size_t pos = name.find_last_of('.');
if (pos != std::string::npos && name.compare(pos, name.size()-pos, ".nif") == 0)
loadBonesFromFile(node, name, resourceSystem);
}
else
break;
++found;
}
}
enum InjectType
{
None,
Model,
ModelWithFallback
};
osg::ref_ptr<osg::Node> getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, InjectType inject)
{
Resource::SceneManager* sceneMgr = resourceSystem->getSceneManager();
if (baseonly)
{
typedef std::map<std::string, osg::ref_ptr<osg::Node> > Cache;
@ -1347,6 +1424,12 @@ namespace MWRender
{
osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);
if (inject == InjectType::ModelWithFallback)
injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem);
if (inject != InjectType::None)
injectCustomBones(created, model, resourceSystem);
SceneUtil::CleanObjectRootVisitor removeDrawableVisitor;
created->accept(removeDrawableVisitor);
removeDrawableVisitor.remove();
@ -1359,7 +1442,17 @@ namespace MWRender
return sceneMgr->createInstance(found->second);
}
else
return sceneMgr->getInstance(model);
{
osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);
if (inject == InjectType::ModelWithFallback)
injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem);
if (inject != InjectType::None)
injectCustomBones(created, model, resourceSystem);
return created;
}
}
void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature)
@ -1381,9 +1474,18 @@ namespace MWRender
mAccumRoot = nullptr;
mAccumCtrl = nullptr;
static const bool useAdditionalSources = Settings::Manager::getBool ("use additional anim sources", "Game");
InjectType inject = useAdditionalSources && mPtr.getClass().isActor() ? InjectType::Model : InjectType::None;
if (inject != InjectType::None && isCreature)
{
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
if(ref->mBase->mFlags & ESM::Creature::Bipedal)
inject = InjectType::ModelWithFallback;
}
if (!forceskeleton)
{
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem->getSceneManager(), model, baseonly);
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject);
mInsert->addChild(created);
mObjectRoot = created->asGroup();
if (!mObjectRoot)
@ -1399,7 +1501,7 @@ namespace MWRender
}
else
{
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem->getSceneManager(), model, baseonly);
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject);
osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(created.get());
if (!skel)
{

@ -24,6 +24,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "npcanimation.hpp"
#include "vismask.hpp"
@ -290,55 +291,36 @@ namespace MWRender
MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter);
MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
std::string groupname;
std::string groupname = "inventoryhandtohand";
bool showCarriedLeft = true;
if(iter == inv.end())
groupname = "inventoryhandtohand";
else
if(iter != inv.end())
{
const std::string &typeName = iter->getTypeName();
if(typeName == typeid(ESM::Lockpick).name() || typeName == typeid(ESM::Probe).name())
groupname = "inventoryweapononehand";
else if(typeName == typeid(ESM::Weapon).name())
groupname = "inventoryweapononehand";
if(iter->getTypeName() == typeid(ESM::Weapon).name())
{
MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>();
int type = ref->mBase->mData.mType;
if(type == ESM::Weapon::ShortBladeOneHand ||
type == ESM::Weapon::LongBladeOneHand ||
type == ESM::Weapon::BluntOneHand ||
type == ESM::Weapon::AxeOneHand ||
type == ESM::Weapon::MarksmanThrown)
{
groupname = "inventoryweapononehand";
}
else if(type == ESM::Weapon::MarksmanCrossbow ||
type == ESM::Weapon::MarksmanBow)
{
groupname = "inventoryweapononehand";
showCarriedLeft = false;
}
else if(type == ESM::Weapon::LongBladeTwoHand ||
type == ESM::Weapon::BluntTwoClose ||
type == ESM::Weapon::AxeTwoHand)
{
groupname = "inventoryweapontwohand";
showCarriedLeft = false;
}
else if(type == ESM::Weapon::BluntTwoWide ||
type == ESM::Weapon::SpearTwoWide)
{
groupname = "inventoryweapontwowide";
showCarriedLeft = false;
}
const ESM::WeaponType* weaponInfo = MWMechanics::getWeaponType(type);
showCarriedLeft = !(weaponInfo->mFlags & ESM::WeaponType::TwoHanded);
std::string inventoryGroup = weaponInfo->mLongGroup;
inventoryGroup = "inventory" + inventoryGroup;
// We still should use one-handed animation as fallback
if (mAnimation->hasAnimation(inventoryGroup))
groupname = inventoryGroup;
else
{
groupname = "inventoryhandtohand";
showCarriedLeft = false;
static const std::string oneHandFallback = "inventory" + MWMechanics::getWeaponType(ESM::Weapon::LongBladeOneHand)->mLongGroup;
static const std::string twoHandFallback = "inventory" + MWMechanics::getWeaponType(ESM::Weapon::LongBladeTwoHand)->mLongGroup;
// For real two-handed melee weapons use 2h swords animations as fallback, otherwise use the 1h ones
if (weaponInfo->mFlags & ESM::WeaponType::TwoHanded && weaponInfo->mWeaponClass == ESM::WeaponType::Melee)
groupname = twoHandFallback;
else
groupname = oneHandFallback;
}
}
else
groupname = "inventoryhandtohand";
}
mAnimation->showCarriedLeft(showCarriedLeft);

@ -15,6 +15,8 @@
#include "../mwbase/world.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "../mwworld/class.hpp"
namespace MWRender
@ -50,9 +52,6 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
{
if (mWeaponSheathing)
injectWeaponBones();
addAnimSource("meshes\\xbase_anim.nif", model);
}
addAnimSource(model, model);
@ -115,7 +114,22 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
std::string bonename;
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
bonename = "Weapon Bone";
{
if(item.getTypeName() == typeid(ESM::Weapon).name())
{
int type = item.get<ESM::Weapon>()->mBase->mData.mType;
bonename = MWMechanics::getWeaponType(type)->mAttachBone;
if (bonename != "Weapon Bone")
{
const NodeMap& nodeMap = getNodeMap();
NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));
if (found == nodeMap.end())
bonename = "Weapon Bone";
}
}
else
bonename = "Weapon Bone";
}
else
bonename = "Shield Bone";
@ -140,8 +154,9 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
item.getTypeName() == typeid(ESM::Weapon).name() &&
item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
{
const ESM::WeaponType* weaponInfo = MWMechanics::getWeaponType(ESM::Weapon::MarksmanCrossbow);
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == weaponInfo->mAmmoType)
attachArrow();
else
mAmmunition.reset();
@ -197,7 +212,19 @@ osg::Group *CreatureWeaponAnimation::getArrowBone()
if (!mWeapon)
return nullptr;
SceneUtil::FindByNameVisitor findVisitor ("ArrowBone");
if (!mPtr.getClass().hasInventoryStore(mPtr))
return nullptr;
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())
return nullptr;
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
int ammoType = MWMechanics::getWeaponType(type)->mAmmoType;
SceneUtil::FindByNameVisitor findVisitor (MWMechanics::getWeaponType(ammoType)->mAttachBone);
mWeapon->getNode()->accept(findVisitor);
return findVisitor.mFoundNode;

@ -31,6 +31,7 @@
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -275,7 +276,7 @@ static NpcAnimation::PartBoneMap createPartListMap()
result.insert(std::make_pair(ESM::PRT_LLeg, "Left Upper Leg"));
result.insert(std::make_pair(ESM::PRT_RPauldron, "Right Clavicle"));
result.insert(std::make_pair(ESM::PRT_LPauldron, "Left Clavicle"));
result.insert(std::make_pair(ESM::PRT_Weapon, "Weapon Bone"));
result.insert(std::make_pair(ESM::PRT_Weapon, "Weapon Bone")); // Fallback. The real node name depends on the current weapon type.
result.insert(std::make_pair(ESM::PRT_Tail, "Tail"));
return result;
}
@ -318,12 +319,6 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
if(mViewMode == viewMode)
return;
// Disable weapon sheathing in the 1st-person mode
if (viewMode == VM_FirstPerson)
mWeaponSheathing = false;
else
mWeaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game");
mViewMode = viewMode;
MWBase::Environment::get().getWorld()->scaleObject(mPtr, mPtr.getCellRef().getScale()); // apply race height after view change
@ -485,9 +480,6 @@ void NpcAnimation::updateNpcBase()
setObjectRoot(smodel, true, true, false);
if (mWeaponSheathing)
injectWeaponBones();
updateParts();
if(!is1stPerson)
@ -745,7 +737,26 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
mPartPriorities[type] = priority;
try
{
const std::string& bonename = sPartList.at(type);
std::string bonename = sPartList.at(type);
if (type == ESM::PRT_Weapon)
{
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name())
{
int weaponType = weapon->get<ESM::Weapon>()->mBase->mData.mType;
const std::string weaponBonename = MWMechanics::getWeaponType(weaponType)->mAttachBone;
if (weaponBonename != bonename)
{
const NodeMap& nodeMap = getNodeMap();
NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(weaponBonename));
if (found != nodeMap.end())
bonename = weaponBonename;
}
}
}
// PRT_Hair seems to be the only type that breaks consistency and uses a filter that's different from the attachment bone
const std::string bonefilter = (type == ESM::PRT_Hair) ? "hair" : bonename;
mObjectParts[type] = insertBoundedPart(mesh, bonename, bonefilter, enchantedGlow, glowColor);
@ -906,8 +917,9 @@ void NpcAnimation::showWeapons(bool showWeapon)
if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
{
int ammotype = MWMechanics::getWeaponType(ESM::Weapon::MarksmanCrossbow)->mAmmoType;
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ammotype)
attachArrow();
}
}
@ -972,7 +984,15 @@ osg::Group* NpcAnimation::getArrowBone()
if (!part)
return nullptr;
SceneUtil::FindByNameVisitor findVisitor ("ArrowBone");
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name())
return nullptr;
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
int ammoType = MWMechanics::getWeaponType(type)->mAmmoType;
SceneUtil::FindByNameVisitor findVisitor (MWMechanics::getWeaponType(ammoType)->mAttachBone);
part->getNode()->accept(findVisitor);
return findVisitor.mFoundNode;

@ -15,6 +15,7 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/combat.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "animation.hpp"
#include "rotatecontroller.hpp"
@ -67,8 +68,10 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
return;
if (weaponSlot->getTypeName() != typeid(ESM::Weapon).name())
return;
int weaponType = weaponSlot->get<ESM::Weapon>()->mBase->mData.mType;
if (weaponType == ESM::Weapon::MarksmanThrown)
int type = weaponSlot->get<ESM::Weapon>()->mBase->mData.mType;
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;
if (weapclass == ESM::WeaponType::Thrown)
{
std::string soundid = weaponSlot->getClass().getUpSoundId(*weaponSlot);
if(!soundid.empty())
@ -78,7 +81,7 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
}
showWeapon(true);
}
else if (weaponType == ESM::Weapon::MarksmanBow || weaponType == ESM::Weapon::MarksmanCrossbow)
else if (weapclass == ESM::WeaponType::Ranged)
{
osg::Group* parent = getArrowBone();
if (!parent)
@ -113,7 +116,7 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
MWMechanics::applyFatigueLoss(actor, *weapon, attackStrength);
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
if (MWMechanics::getWeaponType(weapon->get<ESM::Weapon>()->mBase->mData.mType)->mWeaponClass == ESM::WeaponType::Thrown)
{
// Thrown weapons get detached now
osg::Node* weaponNode = getWeaponNode();

@ -16,7 +16,7 @@
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "esmstore.hpp"
#include "class.hpp"
@ -332,7 +332,7 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase;
if (esmWeapon->mData.mType == ESM::Weapon::Arrow || esmWeapon->mData.mType == ESM::Weapon::Bolt)
if (MWMechanics::getWeaponType(esmWeapon->mData.mType)->mWeaponClass == ESM::WeaponType::Ammo)
continue;
if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill])
@ -357,31 +357,21 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
}
}
bool isBow = false;
bool isCrossbow = false;
if (weapon != end())
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType;
if (type == ESM::Weapon::MarksmanBow)
isBow = true;
else if (type == ESM::Weapon::MarksmanCrossbow)
isCrossbow = true;
}
if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first)
{
// Do not equip ranged weapons, if there is no suitable ammo
bool hasAmmo = true;
if (isBow == true)
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
int type = ref->mBase->mData.mType;
int ammotype = MWMechanics::getWeaponType(type)->mAmmoType;
if (ammotype == ESM::Weapon::Arrow)
{
if (arrow == end())
hasAmmo = false;
else
slots_[Slot_Ammunition] = arrow;
}
if (isCrossbow == true)
else if (ammotype == ESM::Weapon::Bolt)
{
if (bolt == end())
hasAmmo = false;
@ -406,7 +396,7 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
int slot = itemsSlots.first.front();
slots_[slot] = weapon;
if (!isBow && !isCrossbow)
if (ammotype == ESM::Weapon::None)
slots_[Slot_Ammunition] = end();
}

@ -32,6 +32,7 @@
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/aipackage.hpp"
#include "../mwmechanics/weapontype.hpp"
#include "../mwrender/animation.hpp"
#include "../mwrender/vismask.hpp"
@ -317,7 +318,9 @@ namespace MWWorld
state.mIdArrow = projectile.getCellRef().getRefId();
state.mCasterHandle = actor;
state.mAttackStrength = attackStrength;
state.mThrown = projectile.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown;
int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().getRefId());
MWWorld::Ptr ptr = ref.getPtr();
@ -604,7 +607,9 @@ namespace MWWorld
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
state.mThrown = ptr.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown;
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
}
catch(...)
{

@ -388,6 +388,30 @@ namespace MWWorld
iterator end() const;
};
template <>
class Store<ESM::WeaponType> : public StoreBase
{
std::map<int, ESM::WeaponType> mStatic;
public:
typedef std::map<int, ESM::WeaponType>::const_iterator iterator;
Store();
const ESM::WeaponType *search(const int id) const;
const ESM::WeaponType *find(const int id) const;
RecordId load(ESM::ESMReader &esm) { return RecordId(0, false); }
ESM::WeaponType* insert(const ESM::WeaponType &weaponType);
void setUp();
size_t getSize() const;
iterator begin() const;
iterator end() const;
};
} //end namespace

@ -3,6 +3,8 @@
#include <string>
#include "loadskil.hpp"
namespace ESM
{
@ -21,6 +23,10 @@ struct Weapon
enum Type
{
PickProbe = -4,
HandToHand = -3,
Spell = -2,
None = -1,
ShortBladeOneHand = 0,
LongBladeOneHand = 1,
LongBladeTwoHand = 2,
@ -75,5 +81,34 @@ struct Weapon
void blank();
///< Set record to default state (does not touch the ID).
};
struct WeaponType
{
enum Flags
{
TwoHanded = 0x01,
HasHealth = 0x02
};
enum Class
{
Melee = 0,
Ranged = 1,
Thrown = 2,
Ammo = 3
};
//std::string mDisplayName; // TODO: will be needed later for editor
std::string mShortGroup;
std::string mLongGroup;
std::string mSoundId;
std::string mAttachBone;
std::string mSheathingBone;
ESM::Skill::SkillEnum mSkill;
Class mWeaponClass;
int mAmmoType;
int mFlags;
};
}
#endif

@ -541,6 +541,10 @@ namespace NifOsg
// Marker objects. These meshes are only visible in the editor.
hasMarkers = true;
}
else if(sd->string == "BONE")
{
node->getOrCreateUserDataContainer()->addDescription("CustomBone");
}
}
}

Loading…
Cancel
Save