2011-12-11 22:40:00 -05:00
|
|
|
#include "npcanimation.hpp"
|
|
|
|
|
2015-10-19 22:17:04 +02:00
|
|
|
#include <osg/Depth>
|
2022-09-22 21:26:05 +03:00
|
|
|
#include <osg/MatrixTransform>
|
|
|
|
#include <osg/UserDataContainer>
|
2015-10-19 22:17:04 +02:00
|
|
|
|
2015-12-07 16:29:30 +01:00
|
|
|
#include <osgUtil/CullVisitor>
|
2022-09-22 21:26:05 +03:00
|
|
|
#include <osgUtil/RenderBin>
|
2015-05-31 01:07:43 +02:00
|
|
|
|
2018-08-14 23:05:43 +04:00
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
|
2015-04-22 17:58:55 +02:00
|
|
|
#include <components/misc/rng.hpp>
|
2015-03-15 14:07:47 +13:00
|
|
|
|
2015-01-12 11:29:56 +01:00
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
#include <components/esm3/loadbody.hpp>
|
|
|
|
#include <components/esm3/loadmgef.hpp>
|
|
|
|
#include <components/esm3/loadrace.hpp>
|
2015-04-15 22:11:38 +02:00
|
|
|
#include <components/resource/resourcesystem.hpp>
|
|
|
|
#include <components/resource/scenemanager.hpp>
|
2018-07-17 21:28:05 -05:00
|
|
|
#include <components/sceneutil/actorutil.hpp>
|
2021-11-21 02:25:05 +00:00
|
|
|
#include <components/sceneutil/depth.hpp>
|
2022-09-22 21:26:05 +03:00
|
|
|
#include <components/sceneutil/keyframe.hpp>
|
|
|
|
#include <components/sceneutil/visitor.hpp>
|
2018-03-03 14:16:21 +04:00
|
|
|
#include <components/settings/settings.hpp>
|
|
|
|
|
2019-08-11 15:01:48 +04:00
|
|
|
#include <components/vfs/manager.hpp>
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
#include "../mwworld/class.hpp"
|
2012-10-01 19:17:04 +04:00
|
|
|
#include "../mwworld/esmstore.hpp"
|
2013-02-23 08:03:52 -08:00
|
|
|
#include "../mwworld/inventorystore.hpp"
|
2012-07-03 13:15:20 +02:00
|
|
|
|
2015-08-21 21:12:39 +12:00
|
|
|
#include "../mwmechanics/actorutil.hpp"
|
2022-09-22 21:26:05 +03:00
|
|
|
#include "../mwmechanics/npcstats.hpp"
|
2018-12-26 13:45:28 +04:00
|
|
|
#include "../mwmechanics/weapontype.hpp"
|
2013-08-07 16:03:37 -07:00
|
|
|
|
2012-04-23 15:27:03 +02:00
|
|
|
#include "../mwbase/environment.hpp"
|
2013-04-25 07:08:11 -07:00
|
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
2013-11-15 02:08:36 +01:00
|
|
|
#include "../mwbase/soundmanager.hpp"
|
2022-09-22 21:26:05 +03:00
|
|
|
#include "../mwbase/world.hpp"
|
2012-07-03 12:30:50 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
#include "postprocessor.hpp"
|
2015-10-19 22:17:04 +02:00
|
|
|
#include "renderbin.hpp"
|
2022-09-22 21:26:05 +03:00
|
|
|
#include "rotatecontroller.hpp"
|
|
|
|
#include "vismask.hpp"
|
2012-04-07 22:02:20 -04:00
|
|
|
|
2013-11-21 00:27:22 +01:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
std::string getVampireHead(const ESM::RefId& race, bool female, const VFS::Manager& vfs)
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
static std::map<std::pair<ESM::RefId, int>, const ESM::BodyPart*> sVampireMapping;
|
2013-11-21 00:27:22 +01:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
std::pair<ESM::RefId, int> thisCombination = std::make_pair(race, int(female));
|
2013-11-21 00:27:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (sVampireMapping.find(thisCombination) == sVampireMapping.end())
|
2013-11-21 00:27:22 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
for (const ESM::BodyPart& bodypart : store.get<ESM::BodyPart>())
|
|
|
|
{
|
|
|
|
if (!bodypart.mData.mVampire)
|
|
|
|
continue;
|
|
|
|
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
|
|
|
continue;
|
|
|
|
if (bodypart.mData.mPart != ESM::BodyPart::MP_Head)
|
|
|
|
continue;
|
|
|
|
if (female != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
|
|
|
|
continue;
|
2022-10-03 14:02:59 +02:00
|
|
|
if (!(bodypart.mRace == race))
|
2022-09-22 21:26:05 +03:00
|
|
|
continue;
|
|
|
|
sVampireMapping[thisCombination] = &bodypart;
|
|
|
|
}
|
2013-11-21 00:27:22 +01:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
sVampireMapping.emplace(thisCombination, nullptr);
|
2014-12-11 21:43:31 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination];
|
|
|
|
if (!bodyPart)
|
|
|
|
return std::string();
|
|
|
|
return Misc::ResourceHelpers::correctMeshPath(bodyPart->mModel, &vfs);
|
|
|
|
}
|
2013-11-21 00:27:22 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-01-06 01:59:18 -08:00
|
|
|
namespace MWRender
|
|
|
|
{
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
class HeadAnimationTime : public SceneUtil::ControllerSource
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
MWWorld::Ptr mReference;
|
|
|
|
float mTalkStart;
|
|
|
|
float mTalkStop;
|
|
|
|
float mBlinkStart;
|
|
|
|
float mBlinkStop;
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
float mBlinkTimer;
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool mEnabled;
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
float mValue;
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
private:
|
|
|
|
void resetBlinkTimer();
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
public:
|
|
|
|
HeadAnimationTime(const MWWorld::Ptr& reference);
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void updatePtr(const MWWorld::Ptr& updated);
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void update(float dt);
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void setEnabled(bool enabled);
|
2015-06-22 21:12:15 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void setTalkStart(float value);
|
|
|
|
void setTalkStop(float value);
|
|
|
|
void setBlinkStart(float value);
|
|
|
|
void setBlinkStop(float value);
|
2015-05-31 02:26:31 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
float getValue(osg::NodeVisitor* nv) override;
|
|
|
|
};
|
2014-07-29 00:26:26 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// --------------------------------------------------------------------------------------------------------------
|
2015-05-14 17:34:55 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
HeadAnimationTime::HeadAnimationTime(const MWWorld::Ptr& reference)
|
|
|
|
: mReference(reference)
|
|
|
|
, mTalkStart(0)
|
|
|
|
, mTalkStop(0)
|
|
|
|
, mBlinkStart(0)
|
|
|
|
, mBlinkStop(0)
|
|
|
|
, mEnabled(true)
|
|
|
|
, mValue(0)
|
|
|
|
{
|
|
|
|
resetBlinkTimer();
|
|
|
|
}
|
2014-08-11 05:00:13 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void HeadAnimationTime::updatePtr(const MWWorld::Ptr& updated)
|
|
|
|
{
|
|
|
|
mReference = updated;
|
|
|
|
}
|
2014-07-29 01:16:08 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void HeadAnimationTime::setEnabled(bool enabled)
|
|
|
|
{
|
|
|
|
mEnabled = enabled;
|
|
|
|
}
|
2014-08-11 05:00:13 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void HeadAnimationTime::resetBlinkTimer()
|
2014-07-29 01:16:08 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
|
|
|
mBlinkTimer = -(2.0f + Misc::Rng::rollDice(6, prng));
|
|
|
|
}
|
2014-07-29 01:16:08 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void HeadAnimationTime::update(float dt)
|
|
|
|
{
|
|
|
|
if (!mEnabled)
|
|
|
|
return;
|
2014-07-29 01:16:08 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!MWBase::Environment::get().getSoundManager()->sayActive(mReference))
|
2014-07-29 01:16:08 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
mBlinkTimer += dt;
|
|
|
|
|
|
|
|
float duration = mBlinkStop - mBlinkStart;
|
|
|
|
|
|
|
|
if (mBlinkTimer >= 0 && mBlinkTimer <= duration)
|
|
|
|
{
|
|
|
|
mValue = mBlinkStart + mBlinkTimer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mValue = mBlinkStop;
|
|
|
|
|
|
|
|
if (mBlinkTimer > duration)
|
|
|
|
resetBlinkTimer();
|
2014-07-29 01:16:08 +02:00
|
|
|
}
|
|
|
|
else
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
|
|
|
// FIXME: would be nice to hold on to the SoundPtr so we don't have to retrieve it every frame
|
|
|
|
mValue = mTalkStart
|
|
|
|
+ (mTalkStop - mTalkStart)
|
|
|
|
* std::min(1.f,
|
|
|
|
MWBase::Environment::get().getSoundManager()->getSaySoundLoudness(mReference)
|
|
|
|
* 2); // Rescale a bit (most voices are not very loud)
|
|
|
|
}
|
|
|
|
}
|
2014-07-29 01:16:08 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
float HeadAnimationTime::getValue(osg::NodeVisitor*)
|
|
|
|
{
|
|
|
|
return mValue;
|
2014-07-29 01:16:08 +02:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
|
|
|
|
void HeadAnimationTime::setTalkStart(float value)
|
2014-07-29 01:16:08 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
mTalkStart = value;
|
2014-07-29 01:16:08 +02:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void HeadAnimationTime::setTalkStop(float value)
|
|
|
|
{
|
|
|
|
mTalkStop = value;
|
|
|
|
}
|
2014-07-29 00:26:26 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void HeadAnimationTime::setBlinkStart(float value)
|
|
|
|
{
|
|
|
|
mBlinkStart = value;
|
|
|
|
}
|
2014-07-29 00:26:26 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void HeadAnimationTime::setBlinkStop(float value)
|
|
|
|
{
|
|
|
|
mBlinkStop = value;
|
|
|
|
}
|
2014-07-29 00:26:26 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// ----------------------------------------------------
|
2014-07-29 00:26:26 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
NpcAnimation::NpcType NpcAnimation::getNpcType() const
|
|
|
|
{
|
|
|
|
const MWWorld::Class& cls = mPtr.getClass();
|
|
|
|
// Dead vampires should typically stay vampires.
|
|
|
|
if (mNpcType == Type_Vampire && cls.getNpcStats(mPtr).isDead() && !cls.getNpcStats(mPtr).isWerewolf())
|
|
|
|
return mNpcType;
|
|
|
|
return getNpcType(mPtr);
|
|
|
|
}
|
2013-12-07 14:11:06 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
NpcAnimation::NpcType NpcAnimation::getNpcType(const MWWorld::Ptr& ptr)
|
|
|
|
{
|
|
|
|
const MWWorld::Class& cls = ptr.getClass();
|
|
|
|
NpcAnimation::NpcType curType = Type_Normal;
|
|
|
|
if (cls.getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0)
|
|
|
|
curType = Type_Vampire;
|
|
|
|
if (cls.getNpcStats(ptr).isWerewolf())
|
|
|
|
curType = Type_Werewolf;
|
|
|
|
|
|
|
|
return curType;
|
|
|
|
}
|
2015-05-31 02:26:31 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
static const inline NpcAnimation::PartBoneMap createPartListMap()
|
|
|
|
{
|
|
|
|
return { { ESM::PRT_Head, "Head" },
|
|
|
|
{ ESM::PRT_Hair, "Head" }, // note it uses "Head" as attach bone, but "Hair" as filter
|
|
|
|
{ ESM::PRT_Neck, "Neck" }, { ESM::PRT_Cuirass, "Chest" }, { ESM::PRT_Groin, "Groin" },
|
|
|
|
{ ESM::PRT_Skirt, "Groin" }, { ESM::PRT_RHand, "Right Hand" }, { ESM::PRT_LHand, "Left Hand" },
|
|
|
|
{ ESM::PRT_RWrist, "Right Wrist" }, { ESM::PRT_LWrist, "Left Wrist" }, { ESM::PRT_Shield, "Shield Bone" },
|
|
|
|
{ ESM::PRT_RForearm, "Right Forearm" }, { ESM::PRT_LForearm, "Left Forearm" },
|
|
|
|
{ ESM::PRT_RUpperarm, "Right Upper Arm" }, { ESM::PRT_LUpperarm, "Left Upper Arm" },
|
|
|
|
{ ESM::PRT_RFoot, "Right Foot" }, { ESM::PRT_LFoot, "Left Foot" }, { ESM::PRT_RAnkle, "Right Ankle" },
|
|
|
|
{ ESM::PRT_LAnkle, "Left Ankle" }, { ESM::PRT_RKnee, "Right Knee" }, { ESM::PRT_LKnee, "Left Knee" },
|
|
|
|
{ ESM::PRT_RLeg, "Right Upper Leg" }, { ESM::PRT_LLeg, "Left Upper Leg" },
|
|
|
|
{ ESM::PRT_RPauldron, "Right Clavicle" }, { ESM::PRT_LPauldron, "Left Clavicle" },
|
|
|
|
{ ESM::PRT_Weapon, "Weapon Bone" }, // Fallback. The real node name depends on the current weapon type.
|
|
|
|
{ ESM::PRT_Tail, "Tail" } };
|
|
|
|
}
|
|
|
|
const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();
|
2020-05-01 17:10:06 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
NpcAnimation::~NpcAnimation()
|
|
|
|
{
|
|
|
|
mAmmunition.reset();
|
|
|
|
}
|
2019-03-20 11:52:47 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode,
|
|
|
|
Resource::ResourceSystem* resourceSystem, bool disableSounds, ViewMode viewMode, float firstPersonFieldOfView)
|
|
|
|
: ActorAnimation(ptr, parentNode, resourceSystem)
|
|
|
|
, mViewMode(viewMode)
|
|
|
|
, mShowWeapons(false)
|
|
|
|
, mShowCarriedLeft(true)
|
|
|
|
, mNpcType(getNpcType(ptr))
|
|
|
|
, mFirstPersonFieldOfView(firstPersonFieldOfView)
|
|
|
|
, mSoundsDisabled(disableSounds)
|
|
|
|
, mAccurateAiming(false)
|
|
|
|
, mAimingFactor(0.f)
|
|
|
|
{
|
|
|
|
mNpc = mPtr.get<ESM::NPC>()->mBase;
|
2013-01-06 01:59:18 -08:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mHeadAnimationTime = std::make_shared<HeadAnimationTime>(mPtr);
|
|
|
|
mWeaponAnimationTime = std::make_shared<WeaponAnimationTime>(this);
|
2011-12-11 22:40:00 -05:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
for (size_t i = 0; i < ESM::PRT_Count; i++)
|
|
|
|
{
|
|
|
|
mPartslots[i] = -1; // each slot is empty
|
|
|
|
mPartPriorities[i] = 0;
|
|
|
|
}
|
2012-07-12 20:12:18 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
std::fill(mSounds.begin(), mSounds.end(), nullptr);
|
2013-12-07 14:11:06 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
updateNpcBase();
|
2012-07-12 20:12:18 -07:00
|
|
|
}
|
2013-11-15 02:08:36 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
2022-08-04 05:32:10 +03:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
assert(viewMode != VM_HeadOnly);
|
|
|
|
if (mViewMode == viewMode)
|
|
|
|
return;
|
|
|
|
// FIXME: sheathing state must be consistent if the third person skeleton doesn't have the necessary node, but
|
|
|
|
// third person skeleton is unavailable in first person view. This is a hack to avoid cosmetic issues.
|
|
|
|
bool viewChange = mViewMode == VM_FirstPerson || viewMode == VM_FirstPerson;
|
|
|
|
mViewMode = viewMode;
|
|
|
|
MWBase::Environment::get().getWorld()->scaleObject(
|
|
|
|
mPtr, mPtr.getCellRef().getScale(), true); // apply race height after view change
|
|
|
|
|
|
|
|
mAmmunition.reset();
|
|
|
|
rebuild();
|
|
|
|
setRenderBin();
|
|
|
|
|
|
|
|
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
|
|
|
|
if (viewChange && shieldSheathing)
|
|
|
|
{
|
|
|
|
int weaptype = ESM::Weapon::None;
|
|
|
|
MWMechanics::getActiveWeapon(mPtr, &weaptype);
|
|
|
|
showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
|
|
|
}
|
2022-08-04 05:32:10 +03:00
|
|
|
}
|
2015-10-19 22:17:04 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
/// @brief A RenderBin callback to clear the depth buffer before rendering.
|
|
|
|
/// Switches depth attachments to a proxy renderbuffer, reattaches original depth then redraws first person root.
|
|
|
|
/// This gives a complete depth buffer which can be used for postprocessing, buffer resolves as if depth was never
|
|
|
|
/// cleared.
|
|
|
|
class DepthClearCallback : public osgUtil::RenderBin::DrawCallback
|
2015-10-19 22:17:04 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
public:
|
|
|
|
DepthClearCallback()
|
|
|
|
{
|
|
|
|
mDepth = new SceneUtil::AutoDepth;
|
|
|
|
mDepth->setWriteMask(true);
|
2021-10-16 18:37:34 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mStateSet = new osg::StateSet;
|
|
|
|
mStateSet->setAttributeAndModes(new osg::ColorMask(false, false, false, false), osg::StateAttribute::ON);
|
|
|
|
mStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
|
|
|
}
|
2015-10-19 22:17:04 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void drawImplementation(
|
|
|
|
osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) override
|
|
|
|
{
|
|
|
|
osg::State* state = renderInfo.getState();
|
2021-10-16 18:37:34 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
|
2021-10-16 18:37:34 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
state->applyAttribute(mDepth);
|
2021-10-16 18:37:34 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
unsigned int frameId = state->getFrameStamp()->getFrameNumber() % 2;
|
2021-10-16 18:37:34 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (postProcessor && postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId))
|
|
|
|
{
|
|
|
|
postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId)->apply(*state);
|
2015-10-19 22:17:04 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
// color accumulation pass
|
|
|
|
bin->drawImplementation(renderInfo, previous);
|
2015-10-19 22:17:04 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
auto primaryFBO = postProcessor->getPrimaryFbo(frameId);
|
2022-05-13 18:58:00 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
|
|
|
|
postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state);
|
|
|
|
else
|
|
|
|
primaryFBO->apply(*state);
|
2021-10-16 18:37:34 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// depth accumulation pass
|
|
|
|
osg::ref_ptr<osg::StateSet> restore = bin->getStateSet();
|
|
|
|
bin->setStateSet(mStateSet);
|
|
|
|
bin->drawImplementation(renderInfo, previous);
|
|
|
|
bin->setStateSet(restore);
|
2022-05-13 18:58:00 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
|
|
|
|
primaryFBO->apply(*state);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// fallback to standard depth clear when we are not rendering our main scene via an intermediate FBO
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
|
|
bin->drawImplementation(renderInfo, previous);
|
|
|
|
}
|
2022-06-21 15:55:06 +00:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
state->checkGLErrors("after DepthClearCallback::drawImplementation");
|
|
|
|
}
|
2015-10-19 22:17:04 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::ref_ptr<osg::Depth> mDepth;
|
|
|
|
osg::ref_ptr<osg::StateSet> mStateSet;
|
|
|
|
};
|
2015-10-19 22:17:04 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
/// Overrides Field of View to given value for rendering the subgraph.
|
|
|
|
/// Must be added as cull callback.
|
|
|
|
class OverrideFieldOfViewCallback : public osg::NodeCallback
|
2015-12-07 16:29:30 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
public:
|
|
|
|
OverrideFieldOfViewCallback(float fov)
|
|
|
|
: mFov(fov)
|
|
|
|
{
|
|
|
|
}
|
2015-12-07 16:29:30 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
|
2015-12-07 16:29:30 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
|
|
|
float fov, aspect, zNear, zFar;
|
|
|
|
if (cv->getProjectionMatrix()->getPerspective(fov, aspect, zNear, zFar) && std::abs(fov - mFov) > 0.001)
|
|
|
|
{
|
|
|
|
fov = mFov;
|
|
|
|
osg::ref_ptr<osg::RefMatrix> newProjectionMatrix = new osg::RefMatrix();
|
|
|
|
newProjectionMatrix->makePerspective(fov, aspect, zNear, zFar);
|
|
|
|
osg::ref_ptr<osg::RefMatrix> invertedOldMatrix = cv->getProjectionMatrix();
|
|
|
|
invertedOldMatrix = new osg::RefMatrix(osg::RefMatrix::inverse(*invertedOldMatrix));
|
|
|
|
osg::ref_ptr<osg::RefMatrix> viewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix());
|
|
|
|
viewMatrix->postMult(*newProjectionMatrix);
|
|
|
|
viewMatrix->postMult(*invertedOldMatrix);
|
|
|
|
cv->pushModelViewMatrix(viewMatrix, osg::Transform::ReferenceFrame::ABSOLUTE_RF);
|
|
|
|
traverse(node, nv);
|
|
|
|
cv->popModelViewMatrix();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
traverse(node, nv);
|
2015-12-07 16:29:30 +01:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
private:
|
|
|
|
float mFov;
|
|
|
|
};
|
2015-12-07 16:29:30 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::setRenderBin()
|
2015-10-19 22:17:04 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mViewMode == VM_FirstPerson)
|
2015-10-19 22:17:04 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
static bool prototypeAdded = false;
|
|
|
|
if (!prototypeAdded)
|
|
|
|
{
|
|
|
|
osg::ref_ptr<osgUtil::RenderBin> depthClearBin(new osgUtil::RenderBin);
|
|
|
|
depthClearBin->setDrawCallback(new DepthClearCallback);
|
|
|
|
osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin);
|
|
|
|
prototypeAdded = true;
|
|
|
|
}
|
|
|
|
mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(
|
|
|
|
RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
2015-10-19 22:17:04 +02:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
else if (osg::StateSet* stateset = mObjectRoot->getStateSet())
|
|
|
|
stateset->setRenderBinToInherit();
|
2015-10-19 22:17:04 +02:00
|
|
|
}
|
2013-04-09 15:10:14 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::rebuild()
|
|
|
|
{
|
|
|
|
mScabbard.reset();
|
|
|
|
mHolsteredShield.reset();
|
|
|
|
updateNpcBase();
|
2013-08-07 16:21:57 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
|
|
|
|
}
|
2013-08-07 16:21:57 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
int NpcAnimation::getSlot(const osg::NodePath& path) const
|
2015-05-20 03:35:52 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
for (int i = 0; i < ESM::PRT_Count; ++i)
|
2015-05-20 03:35:52 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const PartHolder* const part = mObjectParts[i].get();
|
|
|
|
if (part == nullptr)
|
|
|
|
continue;
|
|
|
|
if (std::find(path.begin(), path.end(), part->getNode().get()) != path.end())
|
|
|
|
{
|
|
|
|
return mPartslots[i];
|
|
|
|
}
|
2015-05-20 03:35:52 +02:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
return -1;
|
2015-05-20 03:35:52 +02:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::updateNpcBase()
|
|
|
|
{
|
|
|
|
clearAnimSources();
|
|
|
|
for (size_t i = 0; i < ESM::PRT_Count; i++)
|
|
|
|
removeIndividualPart((ESM::PartReferenceType)i);
|
2013-07-08 18:59:51 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
const ESM::Race* race = store.get<ESM::Race>().find(mNpc->mRace);
|
|
|
|
NpcType curType = getNpcType();
|
|
|
|
bool isWerewolf = (curType == Type_Werewolf);
|
|
|
|
bool isVampire = (curType == Type_Vampire);
|
|
|
|
bool isFemale = !mNpc->isMale();
|
2013-08-07 16:03:37 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mHeadModel.clear();
|
|
|
|
mHairModel.clear();
|
2019-03-20 00:50:05 +03:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& headName = isWerewolf ? ESM::RefId::stringRefId("WerewolfHead") : mNpc->mHead;
|
|
|
|
const ESM::RefId& hairName = isWerewolf ? ESM::RefId::stringRefId("WerewolfHair") : mNpc->mHair;
|
2019-03-20 00:50:05 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!headName.empty())
|
|
|
|
{
|
|
|
|
const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(headName);
|
|
|
|
if (bp)
|
|
|
|
mHeadModel = Misc::ResourceHelpers::correctMeshPath(bp->mModel, mResourceSystem->getVFS());
|
|
|
|
else
|
|
|
|
Log(Debug::Warning) << "Warning: Failed to load body part '" << headName << "'";
|
|
|
|
}
|
2013-11-21 00:27:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!hairName.empty())
|
|
|
|
{
|
|
|
|
const ESM::BodyPart* bp = store.get<ESM::BodyPart>().search(hairName);
|
|
|
|
if (bp)
|
|
|
|
mHairModel = Misc::ResourceHelpers::correctMeshPath(bp->mModel, mResourceSystem->getVFS());
|
|
|
|
else
|
|
|
|
Log(Debug::Warning) << "Warning: Failed to load body part '" << hairName << "'";
|
|
|
|
}
|
2013-08-07 16:03:37 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const std::string vampireHead = getVampireHead(mNpc->mRace, isFemale, *mResourceSystem->getVFS());
|
|
|
|
if (!isWerewolf && isVampire && !vampireHead.empty())
|
|
|
|
mHeadModel = vampireHead;
|
2019-03-20 00:50:05 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool is1stPerson = mViewMode == VM_FirstPerson;
|
|
|
|
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
2016-03-18 00:24:38 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
std::string defaultSkeleton = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf);
|
|
|
|
defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS());
|
2018-12-09 00:01:28 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
std::string smodel = defaultSkeleton;
|
|
|
|
if (!is1stPerson && !isWerewolf && !mNpc->mModel.empty())
|
|
|
|
smodel = Misc::ResourceHelpers::correctActorModelPath(
|
|
|
|
Misc::ResourceHelpers::correctMeshPath(mNpc->mModel, mResourceSystem->getVFS()),
|
|
|
|
mResourceSystem->getVFS());
|
2015-04-23 23:30:06 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
setObjectRoot(smodel, true, true, false);
|
2013-05-07 16:59:32 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
updateParts();
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!is1stPerson)
|
|
|
|
{
|
|
|
|
const std::string& base = Settings::Manager::getString("xbaseanim", "Models");
|
|
|
|
if (smodel != base && !isWerewolf)
|
|
|
|
addAnimSource(base, smodel);
|
2016-03-18 00:16:46 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (smodel != defaultSkeleton && base != defaultSkeleton)
|
|
|
|
addAnimSource(defaultSkeleton, smodel);
|
2018-12-09 00:01:28 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
addAnimSource(smodel, smodel);
|
2016-03-18 00:16:46 +01:00
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
if (!isWerewolf
|
|
|
|
&& Misc::StringUtils::lowerCase(mNpc->mRace.getRefIdString()).find("argonian") != std::string::npos)
|
2022-09-22 21:26:05 +03:00
|
|
|
addAnimSource("meshes\\xargonian_swimkna.nif", smodel);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const std::string& base = Settings::Manager::getString("xbaseanim1st", "Models");
|
|
|
|
if (smodel != base && !isWerewolf)
|
|
|
|
addAnimSource(base, smodel);
|
|
|
|
|
|
|
|
addAnimSource(smodel, smodel);
|
2016-03-18 00:24:38 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mObjectRoot->setNodeMask(Mask_FirstPerson);
|
|
|
|
mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView));
|
|
|
|
}
|
2016-03-18 00:24:38 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mWeaponAnimationTime->updateStartTime();
|
2013-04-09 15:10:14 -07:00
|
|
|
}
|
2013-05-07 16:59:32 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
std::string NpcAnimation::getSheathedShieldMesh(const MWWorld::ConstPtr& shield) const
|
|
|
|
{
|
|
|
|
std::string mesh = getShieldMesh(shield, !mNpc->isMale());
|
2012-01-17 15:10:53 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mesh.empty())
|
|
|
|
return std::string();
|
2019-08-11 15:01:48 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
std::string holsteredName = mesh;
|
|
|
|
holsteredName = holsteredName.replace(holsteredName.size() - 4, 4, "_sh.nif");
|
|
|
|
if (mResourceSystem->getVFS()->exists(holsteredName))
|
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::Node> shieldTemplate = mResourceSystem->getSceneManager()->getInstance(holsteredName);
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor("Bip01 Sheath");
|
|
|
|
shieldTemplate->accept(findVisitor);
|
|
|
|
osg::ref_ptr<osg::Node> sheathNode = findVisitor.mFoundNode;
|
|
|
|
if (!sheathNode)
|
|
|
|
return std::string();
|
|
|
|
}
|
2020-01-10 13:00:57 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
return mesh;
|
2019-08-11 15:01:48 +04:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::updateParts()
|
|
|
|
{
|
|
|
|
if (!mObjectRoot.get())
|
|
|
|
return;
|
2019-08-11 15:01:48 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
NpcType curType = getNpcType();
|
|
|
|
if (curType != mNpcType)
|
|
|
|
{
|
|
|
|
mNpcType = curType;
|
|
|
|
rebuild();
|
|
|
|
return;
|
|
|
|
}
|
2015-03-02 15:19:57 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
static const struct
|
|
|
|
{
|
|
|
|
int mSlot;
|
|
|
|
int mBasePriority;
|
|
|
|
} slotlist[] = { // FIXME: Priority is based on the number of reserved slots. There should be a better way.
|
|
|
|
{ MWWorld::InventoryStore::Slot_Robe, 11 }, { MWWorld::InventoryStore::Slot_Skirt, 3 },
|
|
|
|
{ MWWorld::InventoryStore::Slot_Helmet, 0 }, { MWWorld::InventoryStore::Slot_Cuirass, 0 },
|
|
|
|
{ MWWorld::InventoryStore::Slot_Greaves, 0 }, { MWWorld::InventoryStore::Slot_LeftPauldron, 0 },
|
|
|
|
{ MWWorld::InventoryStore::Slot_RightPauldron, 0 }, { MWWorld::InventoryStore::Slot_Boots, 0 },
|
|
|
|
{ MWWorld::InventoryStore::Slot_LeftGauntlet, 0 }, { MWWorld::InventoryStore::Slot_RightGauntlet, 0 },
|
|
|
|
{ MWWorld::InventoryStore::Slot_Shirt, 0 }, { MWWorld::InventoryStore::Slot_Pants, 0 },
|
|
|
|
{ MWWorld::InventoryStore::Slot_CarriedLeft, 0 }, { MWWorld::InventoryStore::Slot_CarriedRight, 0 }
|
|
|
|
};
|
|
|
|
static const size_t slotlistsize = sizeof(slotlist) / sizeof(slotlist[0]);
|
2014-01-04 20:43:57 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool wasArrowAttached = isArrowAttached();
|
|
|
|
mAmmunition.reset();
|
2013-01-09 07:43:10 -08:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
for (size_t i = 0; i < slotlistsize && mViewMode != VM_HeadOnly; i++)
|
|
|
|
{
|
|
|
|
MWWorld::ConstContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot);
|
2014-12-30 16:22:06 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
removePartGroup(slotlist[i].mSlot);
|
2012-04-15 22:06:05 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (store == inv.end())
|
|
|
|
continue;
|
2012-01-17 15:10:53 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet)
|
|
|
|
removeIndividualPart(ESM::PRT_Hair);
|
2012-04-15 22:06:05 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
int prio = 1;
|
|
|
|
bool enchantedGlow = !store->getClass().getEnchantment(*store).empty();
|
|
|
|
osg::Vec4f glowColor = store->getClass().getEnchantmentColor(*store);
|
|
|
|
if (store->getType() == ESM::Clothing::sRecordId)
|
|
|
|
{
|
|
|
|
prio = ((slotlist[i].mBasePriority + 1) << 1) + 0;
|
|
|
|
const ESM::Clothing* clothes = store->get<ESM::Clothing>()->mBase;
|
|
|
|
addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts, enchantedGlow, &glowColor);
|
|
|
|
}
|
|
|
|
else if (store->getType() == ESM::Armor::sRecordId)
|
|
|
|
{
|
|
|
|
prio = ((slotlist[i].mBasePriority + 1) << 1) + 1;
|
|
|
|
const ESM::Armor* armor = store->get<ESM::Armor>()->mBase;
|
|
|
|
addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts, enchantedGlow, &glowColor);
|
|
|
|
}
|
2012-04-13 23:25:15 -04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe)
|
|
|
|
{
|
|
|
|
ESM::PartReferenceType parts[] = { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,
|
|
|
|
ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, ESM::PRT_RForearm,
|
|
|
|
ESM::PRT_LForearm, ESM::PRT_Cuirass };
|
|
|
|
size_t parts_size = sizeof(parts) / sizeof(parts[0]);
|
|
|
|
for (size_t p = 0; p < parts_size; ++p)
|
|
|
|
reserveIndividualPart(parts[p], slotlist[i].mSlot, prio);
|
|
|
|
}
|
|
|
|
else if (slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt)
|
|
|
|
{
|
|
|
|
reserveIndividualPart(ESM::PRT_Groin, slotlist[i].mSlot, prio);
|
|
|
|
reserveIndividualPart(ESM::PRT_RLeg, slotlist[i].mSlot, prio);
|
|
|
|
reserveIndividualPart(ESM::PRT_LLeg, slotlist[i].mSlot, prio);
|
|
|
|
}
|
2012-07-12 20:12:18 -07:00
|
|
|
}
|
2012-01-17 15:10:53 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mViewMode != VM_FirstPerson)
|
2013-04-25 23:42:20 -07:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mPartPriorities[ESM::PRT_Head] < 1 && !mHeadModel.empty())
|
|
|
|
addOrReplaceIndividualPart(ESM::PRT_Head, -1, 1, mHeadModel);
|
|
|
|
if (mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1 && !mHairModel.empty())
|
|
|
|
addOrReplaceIndividualPart(ESM::PRT_Hair, -1, 1, mHairModel);
|
2013-04-25 23:42:20 -07:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mViewMode == VM_HeadOnly)
|
|
|
|
return;
|
2012-04-15 22:06:05 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mPartPriorities[ESM::PRT_Shield] < 1)
|
2013-08-04 18:58:10 -07:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
MWWorld::ConstContainerStoreIterator store = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
|
|
MWWorld::ConstPtr part;
|
|
|
|
if (store != inv.end() && (part = *store).getType() == ESM::Light::sRecordId)
|
|
|
|
{
|
|
|
|
const ESM::Light* light = part.get<ESM::Light>()->mBase;
|
|
|
|
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
|
|
|
addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1,
|
|
|
|
Misc::ResourceHelpers::correctMeshPath(light->mModel, vfs), false, nullptr, true);
|
|
|
|
if (mObjectParts[ESM::PRT_Shield])
|
|
|
|
addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), light);
|
|
|
|
}
|
2013-08-04 18:58:10 -07:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
showWeapons(mShowWeapons);
|
|
|
|
showCarriedLeft(mShowCarriedLeft);
|
2013-04-29 09:53:36 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool isWerewolf = (getNpcType() == Type_Werewolf);
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
ESM::RefId race = (isWerewolf ? ESM::RefId::stringRefId("werewolf") : mNpc->mRace);
|
2014-06-06 01:30:23 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const std::vector<const ESM::BodyPart*>& parts
|
|
|
|
= getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf);
|
|
|
|
for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part)
|
2013-07-12 22:30:25 -07:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mPartPriorities[part] < 1)
|
2022-06-29 00:32:11 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const ESM::BodyPart* bodypart = parts[part];
|
|
|
|
if (bodypart)
|
|
|
|
{
|
|
|
|
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
|
|
|
addOrReplaceIndividualPart(static_cast<ESM::PartReferenceType>(part), -1, 1,
|
|
|
|
Misc::ResourceHelpers::correctMeshPath(bodypart->mModel, vfs));
|
|
|
|
}
|
2022-06-29 00:32:11 +02:00
|
|
|
}
|
2013-07-12 22:30:25 -07:00
|
|
|
}
|
2014-12-30 16:22:06 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (wasArrowAttached)
|
|
|
|
attachArrow();
|
|
|
|
}
|
2013-10-02 05:16:52 -04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, std::string_view bonename,
|
|
|
|
std::string_view bonefilter, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight)
|
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::Node> attached = attach(model, bonename, bonefilter, isLight);
|
|
|
|
if (enchantedGlow)
|
|
|
|
mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor);
|
2017-02-03 23:15:37 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
return std::make_unique<PartHolder>(attached);
|
|
|
|
}
|
2017-02-03 23:15:37 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Vec3f NpcAnimation::runAnimation(float timepassed)
|
|
|
|
{
|
|
|
|
osg::Vec3f ret = Animation::runAnimation(timepassed);
|
2015-04-19 01:57:52 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mHeadAnimationTime->update(timepassed);
|
2015-04-15 22:11:38 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mFirstPersonNeckController)
|
|
|
|
{
|
|
|
|
if (mAccurateAiming)
|
|
|
|
mAimingFactor = 1.f;
|
|
|
|
else
|
|
|
|
mAimingFactor = std::max(0.f, mAimingFactor - timepassed * 0.5f);
|
2013-04-15 18:55:28 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
float rotateFactor = 0.75f + 0.25f * mAimingFactor;
|
2014-07-29 01:16:08 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mFirstPersonNeckController->setRotate(
|
|
|
|
osg::Quat(mPtr.getRefData().getPosition().rot[0] * rotateFactor, osg::Vec3f(-1, 0, 0)));
|
|
|
|
mFirstPersonNeckController->setOffset(mFirstPersonOffset);
|
|
|
|
}
|
2015-11-10 01:01:41 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians());
|
2015-11-10 01:01:41 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
return ret;
|
2015-05-31 02:26:31 +02:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type)
|
|
|
|
{
|
|
|
|
mPartPriorities[type] = 0;
|
|
|
|
mPartslots[type] = -1;
|
2012-04-12 20:40:11 -04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mObjectParts[type].reset();
|
|
|
|
if (mSounds[type] != nullptr && !mSoundsDisabled)
|
|
|
|
{
|
|
|
|
MWBase::Environment::get().getSoundManager()->stopSound(mSounds[type]);
|
|
|
|
mSounds[type] = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2012-04-15 22:06:05 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::reserveIndividualPart(ESM::PartReferenceType type, int group, int priority)
|
2014-10-11 21:05:12 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
if (priority > mPartPriorities[type])
|
|
|
|
{
|
|
|
|
removeIndividualPart(type);
|
|
|
|
mPartPriorities[type] = priority;
|
|
|
|
mPartslots[type] = group;
|
|
|
|
}
|
2014-10-11 21:05:12 +02:00
|
|
|
}
|
2012-04-12 20:40:11 -04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::removePartGroup(int group)
|
2012-07-12 20:12:18 -07:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
for (int i = 0; i < ESM::PRT_Count; i++)
|
|
|
|
{
|
|
|
|
if (mPartslots[i] == group)
|
|
|
|
removeIndividualPart((ESM::PartReferenceType)i);
|
|
|
|
}
|
2012-04-12 20:59:30 -04:00
|
|
|
}
|
2012-04-12 22:16:02 -04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool NpcAnimation::isFirstPersonPart(const ESM::BodyPart* bodypart)
|
2012-07-12 20:12:18 -07:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const auto partName = bodypart->mId.getRefIdString();
|
|
|
|
return partName.size() >= 3 && partName.substr(partName.size() - 3, 3) == "1st";
|
2012-07-12 20:12:18 -07:00
|
|
|
}
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool NpcAnimation::isFemalePart(const ESM::BodyPart* bodypart)
|
|
|
|
{
|
|
|
|
return bodypart->mData.mFlags & ESM::BodyPart::BPF_Female;
|
|
|
|
}
|
2012-04-14 18:58:52 -04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority,
|
|
|
|
const std::string& mesh, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight)
|
2014-12-03 00:02:14 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
if (priority <= mPartPriorities[type])
|
|
|
|
return false;
|
|
|
|
|
|
|
|
removeIndividualPart(type);
|
|
|
|
mPartslots[type] = group;
|
|
|
|
mPartPriorities[type] = priority;
|
|
|
|
try
|
2018-12-26 13:45:28 +04:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
std::string_view bonename = sPartList.at(type);
|
|
|
|
if (type == ESM::PRT_Weapon)
|
2018-12-26 13:45:28 +04:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
|
|
if (weapon != inv.end() && weapon->getType() == ESM::Weapon::sRecordId)
|
2019-08-09 10:00:40 +04:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
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(weaponBonename);
|
|
|
|
if (found != nodeMap.end())
|
|
|
|
bonename = weaponBonename;
|
|
|
|
}
|
2019-08-09 10:00:40 +04:00
|
|
|
}
|
2018-12-26 13:45:28 +04:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// 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_view bonefilter = (type == ESM::PRT_Hair) ? std::string_view{ "hair" } : bonename;
|
|
|
|
mObjectParts[type] = insertBoundedPart(mesh, bonename, bonefilter, enchantedGlow, glowColor, isLight);
|
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
|
|
|
Log(Debug::Error) << "Error adding NPC part: " << e.what();
|
|
|
|
return false;
|
|
|
|
}
|
2014-10-12 11:40:14 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!mSoundsDisabled && group == MWWorld::InventoryStore::Slot_CarriedLeft)
|
2014-10-12 11:40:14 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator csi = inv.getSlot(group);
|
|
|
|
if (csi != inv.end())
|
2014-10-12 11:40:14 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const auto soundId = csi->getClass().getSound(*csi);
|
|
|
|
if (!soundId.empty())
|
|
|
|
{
|
|
|
|
mSounds[type] = MWBase::Environment::get().getSoundManager()->playSound3D(
|
|
|
|
mPtr, soundId, 1.0f, 1.0f, MWSound::Type::Sfx, MWSound::PlayMode::Loop);
|
|
|
|
}
|
2014-10-12 11:40:14 +02:00
|
|
|
}
|
|
|
|
}
|
2015-04-19 14:42:09 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Node* node = mObjectParts[type]->getNode();
|
|
|
|
if (node->getNumChildrenRequiringUpdateTraversal() > 0)
|
2015-04-19 14:42:09 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
std::shared_ptr<SceneUtil::ControllerSource> src;
|
|
|
|
if (type == ESM::PRT_Head)
|
2014-07-29 00:26:26 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
src = mHeadAnimationTime;
|
|
|
|
|
|
|
|
if (node->getUserDataContainer())
|
2014-07-29 00:26:26 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
for (unsigned int i = 0; i < node->getUserDataContainer()->getNumUserObjects(); ++i)
|
2015-04-19 14:42:09 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Object* obj = node->getUserDataContainer()->getUserObject(i);
|
|
|
|
if (SceneUtil::TextKeyMapHolder* keys = dynamic_cast<SceneUtil::TextKeyMapHolder*>(obj))
|
2017-02-03 23:10:14 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
for (const auto& key : keys->mTextKeys)
|
|
|
|
{
|
|
|
|
if (Misc::StringUtils::ciEqual(key.second, "talk: start"))
|
|
|
|
mHeadAnimationTime->setTalkStart(key.first);
|
|
|
|
if (Misc::StringUtils::ciEqual(key.second, "talk: stop"))
|
|
|
|
mHeadAnimationTime->setTalkStop(key.first);
|
|
|
|
if (Misc::StringUtils::ciEqual(key.second, "blink: start"))
|
|
|
|
mHeadAnimationTime->setBlinkStart(key.first);
|
|
|
|
if (Misc::StringUtils::ciEqual(key.second, "blink: stop"))
|
|
|
|
mHeadAnimationTime->setBlinkStop(key.first);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2017-02-03 23:10:14 +01:00
|
|
|
}
|
2015-04-19 14:42:09 +02:00
|
|
|
}
|
2014-07-29 00:26:26 +02:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
SceneUtil::ForceControllerSourcesVisitor assignVisitor(src);
|
|
|
|
node->accept(assignVisitor);
|
2014-07-29 00:26:26 +02:00
|
|
|
}
|
2021-08-08 09:06:01 +03:00
|
|
|
else
|
2022-09-22 21:26:05 +03:00
|
|
|
{
|
|
|
|
if (type == ESM::PRT_Weapon)
|
|
|
|
src = mWeaponAnimationTime;
|
|
|
|
else
|
|
|
|
src = std::make_shared<NullAnimationTime>();
|
|
|
|
SceneUtil::AssignControllerSourcesVisitor assignVisitor(src);
|
|
|
|
node->accept(assignVisitor);
|
|
|
|
}
|
2021-08-08 09:06:01 +03:00
|
|
|
}
|
2013-08-05 18:59:55 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
return true;
|
|
|
|
}
|
2013-05-13 19:05:51 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::PartReference>& parts,
|
|
|
|
bool enchantedGlow, osg::Vec4f* glowColor)
|
2012-07-12 20:12:18 -07:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
const MWWorld::Store<ESM::BodyPart>& partStore = store.get<ESM::BodyPart>();
|
|
|
|
|
|
|
|
const char* ext = (mViewMode == VM_FirstPerson) ? ".1st" : "";
|
|
|
|
for (const ESM::PartReference& part : parts)
|
2013-07-13 17:03:10 -07:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const ESM::BodyPart* bodypart = nullptr;
|
|
|
|
if (!mNpc->isMale() && !part.mFemale.empty())
|
2013-07-13 17:03:10 -07:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
bodypart = partStore.search(ESM::RefId::stringRefId(part.mFemale.getRefIdString() + ext));
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!bodypart && mViewMode == VM_FirstPerson)
|
|
|
|
{
|
|
|
|
bodypart = partStore.search(part.mFemale);
|
|
|
|
if (bodypart
|
|
|
|
&& !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand
|
|
|
|
|| bodypart->mData.mPart == ESM::BodyPart::MP_Wrist
|
|
|
|
|| bodypart->mData.mPart == ESM::BodyPart::MP_Forearm
|
|
|
|
|| bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm))
|
|
|
|
bodypart = nullptr;
|
|
|
|
}
|
|
|
|
else if (!bodypart)
|
|
|
|
Log(Debug::Warning) << "Warning: Failed to find body part '" << part.mFemale << "'";
|
2013-07-13 17:03:10 -07:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!bodypart && !part.mMale.empty())
|
2013-07-13 17:03:10 -07:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
bodypart = partStore.search(ESM::RefId::stringRefId(part.mMale.getRefIdString() + ext));
|
2022-09-22 21:26:05 +03:00
|
|
|
if (!bodypart && mViewMode == VM_FirstPerson)
|
|
|
|
{
|
|
|
|
bodypart = partStore.search(part.mMale);
|
|
|
|
if (bodypart
|
|
|
|
&& !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand
|
|
|
|
|| bodypart->mData.mPart == ESM::BodyPart::MP_Wrist
|
|
|
|
|| bodypart->mData.mPart == ESM::BodyPart::MP_Forearm
|
|
|
|
|| bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm))
|
|
|
|
bodypart = nullptr;
|
|
|
|
}
|
|
|
|
else if (!bodypart)
|
|
|
|
Log(Debug::Warning) << "Warning: Failed to find body part '" << part.mMale << "'";
|
2013-07-13 17:03:10 -07:00
|
|
|
}
|
2012-04-15 22:06:05 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (bodypart)
|
|
|
|
{
|
|
|
|
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
|
|
|
addOrReplaceIndividualPart(static_cast<ESM::PartReferenceType>(part.mPart), group, priority,
|
|
|
|
Misc::ResourceHelpers::correctMeshPath(bodypart->mModel, vfs), enchantedGlow, glowColor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
reserveIndividualPart((ESM::PartReferenceType)part.mPart, group, priority);
|
2022-06-29 00:32:11 +02:00
|
|
|
}
|
2012-04-14 18:58:52 -04:00
|
|
|
}
|
2012-07-12 20:12:18 -07:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::addControllers()
|
|
|
|
{
|
|
|
|
Animation::addControllers();
|
2015-06-22 21:06:27 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
mFirstPersonNeckController = nullptr;
|
|
|
|
WeaponAnimation::deleteControllers();
|
2015-05-31 18:53:16 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mViewMode == VM_FirstPerson)
|
2015-05-31 02:26:31 +02:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
NodeMap::iterator found = mNodeMap.find("bip01 neck");
|
|
|
|
if (found != mNodeMap.end())
|
|
|
|
{
|
|
|
|
osg::MatrixTransform* node = found->second.get();
|
|
|
|
mFirstPersonNeckController = new RotateController(mObjectRoot.get());
|
|
|
|
node->addUpdateCallback(mFirstPersonNeckController);
|
|
|
|
mActiveControllers.emplace_back(node, mFirstPersonNeckController);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (mViewMode == VM_Normal)
|
|
|
|
{
|
|
|
|
WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
|
2015-05-31 02:26:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::showWeapons(bool showWeapon)
|
2013-04-28 11:41:01 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
mShowWeapons = showWeapon;
|
|
|
|
mAmmunition.reset();
|
|
|
|
if (showWeapon)
|
2013-04-28 11:41:01 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
|
|
if (weapon != inv.end())
|
2014-02-04 04:11:46 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
|
|
|
|
std::string mesh = weapon->getClass().getModel(*weapon);
|
|
|
|
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh,
|
|
|
|
!weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
|
|
|
|
|
|
|
|
// Crossbows start out with a bolt attached
|
|
|
|
if (weapon->getType() == ESM::Weapon::sRecordId
|
|
|
|
&& 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 == ammotype)
|
|
|
|
attachArrow();
|
|
|
|
}
|
2014-02-04 04:11:46 +01:00
|
|
|
}
|
2013-04-28 11:41:01 +01:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
removeIndividualPart(ESM::PRT_Weapon);
|
|
|
|
// If we remove/hide weapon from player, we should reset attack animation as well
|
|
|
|
if (mPtr == MWMechanics::getPlayer())
|
|
|
|
mPtr.getClass().getCreatureStats(mPtr).setAttackingOrSpell(false);
|
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
updateHolsteredWeapon(!mShowWeapons);
|
|
|
|
updateQuiver();
|
|
|
|
}
|
2013-04-28 11:41:01 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool NpcAnimation::updateCarriedLeftVisible(const int weaptype) const
|
2022-08-04 05:32:10 +03:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
|
|
|
|
if (shieldSheathing)
|
2022-08-04 05:32:10 +03:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::Class& cls = mPtr.getClass();
|
|
|
|
MWMechanics::CreatureStats& stats = cls.getCreatureStats(mPtr);
|
|
|
|
if (stats.getDrawState() == MWMechanics::DrawState::Nothing)
|
2022-08-04 05:32:10 +03:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
SceneUtil::FindByNameVisitor findVisitor("Bip01 AttachShield");
|
|
|
|
mObjectRoot->accept(findVisitor);
|
|
|
|
if (findVisitor.mFoundNode || mViewMode == VM_FirstPerson)
|
|
|
|
{
|
|
|
|
const MWWorld::InventoryStore& inv = cls.getInventoryStore(mPtr);
|
|
|
|
const MWWorld::ConstContainerStoreIterator shield
|
|
|
|
= inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
|
|
if (shield != inv.end() && shield->getType() == ESM::Armor::sRecordId
|
|
|
|
&& !getSheathedShieldMesh(*shield).empty())
|
|
|
|
return false;
|
|
|
|
}
|
2022-08-04 05:32:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
return !(MWMechanics::getWeaponType(weaptype)->mFlags & ESM::WeaponType::TwoHanded);
|
|
|
|
}
|
2022-08-04 05:32:10 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::showCarriedLeft(bool show)
|
2013-11-23 20:24:52 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
mShowCarriedLeft = show;
|
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
|
|
|
if (show && iter != inv.end())
|
2020-02-02 15:39:29 +03:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Vec4f glowColor = iter->getClass().getEnchantmentColor(*iter);
|
|
|
|
std::string mesh = iter->getClass().getModel(*iter);
|
|
|
|
// For shields we must try to use the body part model
|
|
|
|
if (iter->getType() == ESM::Armor::sRecordId)
|
|
|
|
{
|
|
|
|
mesh = getShieldMesh(*iter, !mNpc->isMale());
|
|
|
|
}
|
|
|
|
if (mesh.empty()
|
|
|
|
|| addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh,
|
|
|
|
!iter->getClass().getEnchantment(*iter).empty(), &glowColor,
|
|
|
|
iter->getType() == ESM::Light::sRecordId))
|
|
|
|
{
|
|
|
|
if (mesh.empty())
|
|
|
|
reserveIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1);
|
|
|
|
if (iter->getType() == ESM::Light::sRecordId && mObjectParts[ESM::PRT_Shield])
|
|
|
|
addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), iter->get<ESM::Light>()->mBase);
|
|
|
|
}
|
2020-02-02 15:39:29 +03:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
else
|
|
|
|
removeIndividualPart(ESM::PRT_Shield);
|
|
|
|
|
|
|
|
updateHolsteredShield(mShowCarriedLeft);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NpcAnimation::attachArrow()
|
|
|
|
{
|
|
|
|
WeaponAnimation::attachArrow(mPtr);
|
|
|
|
|
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
|
|
|
if (ammo != inv.end() && !ammo->getClass().getEnchantment(*ammo).empty())
|
2014-01-02 15:58:00 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Group* bone = getArrowBone();
|
|
|
|
if (bone != nullptr && bone->getNumChildren())
|
|
|
|
SceneUtil::addEnchantedGlow(
|
|
|
|
bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo));
|
2014-01-02 15:58:00 +01:00
|
|
|
}
|
2019-08-11 15:01:48 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
updateQuiver();
|
|
|
|
}
|
2013-11-23 20:24:52 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::detachArrow()
|
|
|
|
{
|
|
|
|
WeaponAnimation::detachArrow(mPtr);
|
|
|
|
updateQuiver();
|
|
|
|
}
|
2019-08-07 09:45:10 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::releaseArrow(float attackStrength)
|
2019-08-07 09:45:10 +04:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
WeaponAnimation::releaseArrow(mPtr, attackStrength);
|
|
|
|
updateQuiver();
|
2019-08-07 09:45:10 +04:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Group* NpcAnimation::getArrowBone()
|
|
|
|
{
|
|
|
|
const PartHolder* const part = mObjectParts[ESM::PRT_Weapon].get();
|
|
|
|
if (part == nullptr)
|
|
|
|
return nullptr;
|
2014-02-04 04:11:46 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
|
|
|
MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
|
|
if (weapon == inv.end() || weapon->getType() != ESM::Weapon::sRecordId)
|
|
|
|
return nullptr;
|
2020-08-28 15:28:26 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
int type = weapon->get<ESM::Weapon>()->mBase->mData.mType;
|
|
|
|
int ammoType = MWMechanics::getWeaponType(type)->mAmmoType;
|
|
|
|
if (ammoType == ESM::Weapon::None)
|
|
|
|
return nullptr;
|
2014-02-04 04:11:46 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// Try to find and attachment bone in actor's skeleton, otherwise fall back to the ArrowBone in weapon's mesh
|
|
|
|
osg::Group* bone = getBoneByName(MWMechanics::getWeaponType(ammoType)->mAttachBone);
|
|
|
|
if (bone == nullptr)
|
|
|
|
{
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor("ArrowBone");
|
|
|
|
part->getNode()->accept(findVisitor);
|
|
|
|
bone = findVisitor.mFoundNode;
|
|
|
|
}
|
|
|
|
return bone;
|
2020-09-09 10:14:26 +04:00
|
|
|
}
|
2015-05-31 01:07:43 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
osg::Node* NpcAnimation::getWeaponNode()
|
|
|
|
{
|
|
|
|
const PartHolder* const part = mObjectParts[ESM::PRT_Weapon].get();
|
|
|
|
if (part == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
return part->getNode();
|
|
|
|
}
|
2015-05-31 01:07:43 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
Resource::ResourceSystem* NpcAnimation::getResourceSystem()
|
|
|
|
{
|
|
|
|
return mResourceSystem;
|
|
|
|
}
|
2013-12-26 18:16:28 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::enableHeadAnimation(bool enable)
|
|
|
|
{
|
|
|
|
mHeadAnimationTime->setEnabled(enable);
|
|
|
|
}
|
2013-12-08 23:05:21 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::setWeaponGroup(const std::string& group, bool relativeDuration)
|
2019-08-11 15:01:48 +04:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
mWeaponAnimationTime->setGroup(group, relativeDuration);
|
2019-08-11 15:01:48 +04:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::equipmentChanged()
|
|
|
|
{
|
|
|
|
static const bool shieldSheathing = Settings::Manager::getBool("shield sheathing", "Game");
|
|
|
|
if (shieldSheathing)
|
|
|
|
{
|
|
|
|
int weaptype = ESM::Weapon::None;
|
|
|
|
MWMechanics::getActiveWeapon(mPtr, &weaptype);
|
|
|
|
showCarriedLeft(updateCarriedLeftVisible(weaptype));
|
|
|
|
}
|
2014-10-04 16:47:29 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
updateParts();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NpcAnimation::setVampire(bool vampire)
|
2014-12-12 02:39:59 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
if (mNpcType == Type_Werewolf) // we can't have werewolf vampires, can we
|
|
|
|
return;
|
|
|
|
if ((mNpcType == Type_Vampire) != vampire)
|
|
|
|
{
|
|
|
|
if (mPtr == MWMechanics::getPlayer())
|
|
|
|
MWBase::Environment::get().getWorld()->reattachPlayerCamera();
|
|
|
|
else
|
|
|
|
rebuild();
|
|
|
|
}
|
2014-12-12 02:39:59 +01:00
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::setFirstPersonOffset(const osg::Vec3f& offset)
|
|
|
|
{
|
|
|
|
mFirstPersonOffset = offset;
|
|
|
|
}
|
2015-05-31 02:26:31 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::updatePtr(const MWWorld::Ptr& updated)
|
|
|
|
{
|
|
|
|
Animation::updatePtr(updated);
|
|
|
|
mHeadAnimationTime->updatePtr(updated);
|
|
|
|
}
|
2015-05-14 17:34:55 +02:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
typedef std::map<std::pair<ESM::RefId, int>, std::vector<const ESM::BodyPart*>> RaceMapping;
|
2022-09-22 21:26:05 +03:00
|
|
|
static RaceMapping sRaceMapping;
|
2016-02-09 00:26:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const std::vector<const ESM::BodyPart*>& NpcAnimation::getBodyParts(
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& race, bool female, bool firstPerson, bool werewolf)
|
2016-02-09 00:26:22 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
static const int Flag_FirstPerson = 1 << 1;
|
|
|
|
static const int Flag_Female = 1 << 0;
|
|
|
|
|
|
|
|
int flags = (werewolf ? -1 : 0);
|
|
|
|
if (female)
|
|
|
|
flags |= Flag_Female;
|
|
|
|
if (firstPerson)
|
|
|
|
flags |= Flag_FirstPerson;
|
|
|
|
|
|
|
|
RaceMapping::iterator found = sRaceMapping.find(std::make_pair(race, flags));
|
|
|
|
if (found != sRaceMapping.end())
|
|
|
|
return found->second;
|
|
|
|
else
|
2016-02-09 00:26:22 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
std::vector<const ESM::BodyPart*>& parts = sRaceMapping[std::make_pair(race, flags)];
|
2016-02-09 00:26:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
typedef std::multimap<ESM::BodyPart::MeshPart, ESM::PartReferenceType> BodyPartMapType;
|
|
|
|
static const BodyPartMapType sBodyPartMap = { { ESM::BodyPart::MP_Neck, ESM::PRT_Neck },
|
|
|
|
{ ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass }, { ESM::BodyPart::MP_Groin, ESM::PRT_Groin },
|
|
|
|
{ ESM::BodyPart::MP_Hand, ESM::PRT_RHand }, { ESM::BodyPart::MP_Hand, ESM::PRT_LHand },
|
|
|
|
{ ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist }, { ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist },
|
|
|
|
{ ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm }, { ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm },
|
|
|
|
{ ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm }, { ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm },
|
|
|
|
{ ESM::BodyPart::MP_Foot, ESM::PRT_RFoot }, { ESM::BodyPart::MP_Foot, ESM::PRT_LFoot },
|
|
|
|
{ ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle }, { ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle },
|
|
|
|
{ ESM::BodyPart::MP_Knee, ESM::PRT_RKnee }, { ESM::BodyPart::MP_Knee, ESM::PRT_LKnee },
|
|
|
|
{ ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg }, { ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg },
|
|
|
|
{ ESM::BodyPart::MP_Tail, ESM::PRT_Tail } };
|
2016-02-09 00:26:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
parts.resize(ESM::PRT_Count, nullptr);
|
2019-03-20 00:50:05 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (werewolf)
|
|
|
|
return parts;
|
2019-03-20 00:50:05 +03:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
2016-02-09 00:26:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
for (const ESM::BodyPart& bodypart : store.get<ESM::BodyPart>())
|
|
|
|
{
|
|
|
|
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
|
|
|
|
continue;
|
|
|
|
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
|
|
|
continue;
|
2016-02-09 00:26:22 +01:00
|
|
|
|
2022-10-03 14:02:59 +02:00
|
|
|
if (!(bodypart.mRace == race))
|
2022-09-22 21:26:05 +03:00
|
|
|
continue;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool partFirstPerson = isFirstPersonPart(&bodypart);
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool isHand = bodypart.mData.mPart == ESM::BodyPart::MP_Hand
|
|
|
|
|| bodypart.mData.mPart == ESM::BodyPart::MP_Wrist
|
|
|
|
|| bodypart.mData.mPart == ESM::BodyPart::MP_Forearm
|
|
|
|
|| bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool isSameGender = isFemalePart(&bodypart) == female;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
/* A fallback for the arms if 1st person is missing:
|
|
|
|
1. Try to use 3d person skin for same gender
|
|
|
|
2. Try to use 1st person skin for male, if female == true
|
|
|
|
3. Try to use 3d person skin for male, if female == true
|
|
|
|
|
|
|
|
A fallback in another cases: allow to use male bodyparts, if female == true
|
|
|
|
*/
|
|
|
|
if (firstPerson && isHand && !partFirstPerson)
|
2016-02-09 00:26:22 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
// Allow 3rd person skins as a fallback for the arms if 1st person is missing
|
|
|
|
BodyPartMapType::const_iterator bIt
|
|
|
|
= sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
|
|
|
|
while (bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
|
|
|
|
{
|
|
|
|
// If we have no fallback bodypart now and bodypart is for same gender (1)
|
|
|
|
if (!parts[bIt->second] && isSameGender)
|
|
|
|
parts[bIt->second] = &bodypart;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// If we have fallback bodypart for other gender and found fallback for current gender (1)
|
|
|
|
else if (isSameGender && isFemalePart(parts[bIt->second]) != female)
|
|
|
|
parts[bIt->second] = &bodypart;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// If we have no fallback bodypart and searching for female bodyparts (3)
|
|
|
|
else if (!parts[bIt->second] && female)
|
|
|
|
parts[bIt->second] = &bodypart;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
++bIt;
|
|
|
|
}
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
continue;
|
|
|
|
}
|
2016-02-09 00:26:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// Don't allow to use podyparts for a different view
|
|
|
|
if (partFirstPerson != firstPerson)
|
|
|
|
continue;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
if (female && !isFemalePart(&bodypart))
|
2016-02-09 00:26:22 +01:00
|
|
|
{
|
2022-09-22 21:26:05 +03:00
|
|
|
// Allow male parts as fallback for females if female parts are missing
|
|
|
|
BodyPartMapType::const_iterator bIt
|
|
|
|
= sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
|
|
|
|
while (bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
|
|
|
|
{
|
|
|
|
// If we have no fallback bodypart now
|
|
|
|
if (!parts[bIt->second])
|
|
|
|
parts[bIt->second] = &bodypart;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// If we have 3d person fallback bodypart for hand and 1st person fallback found (2)
|
|
|
|
else if (isHand && !isFirstPersonPart(parts[bIt->second]) && partFirstPerson)
|
|
|
|
parts[bIt->second] = &bodypart;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
++bIt;
|
|
|
|
}
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
continue;
|
|
|
|
}
|
2016-02-09 00:26:22 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// Don't allow to use podyparts for another gender
|
|
|
|
if (female != isFemalePart(&bodypart))
|
|
|
|
continue;
|
2017-04-17 21:53:35 +04:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
// Use properly found bodypart, replacing fallbacks
|
|
|
|
BodyPartMapType::const_iterator bIt
|
|
|
|
= sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
|
|
|
|
while (bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
|
|
|
|
{
|
|
|
|
parts[bIt->second] = &bodypart;
|
|
|
|
++bIt;
|
|
|
|
}
|
2016-02-09 00:26:22 +01:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
return parts;
|
2016-02-09 00:26:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
void NpcAnimation::setAccurateAiming(bool enabled)
|
|
|
|
{
|
|
|
|
mAccurateAiming = enabled;
|
|
|
|
}
|
2015-11-10 01:01:41 +01:00
|
|
|
|
2022-09-22 21:26:05 +03:00
|
|
|
bool NpcAnimation::isArrowAttached() const
|
|
|
|
{
|
|
|
|
return mAmmunition != nullptr;
|
|
|
|
}
|
2018-03-03 14:16:21 +04:00
|
|
|
|
2012-07-12 20:12:18 -07:00
|
|
|
}
|