1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-02 19:15:33 +00:00

Refactor NpcAnimation: get rid of delayed update (no longer required), make sure that the Animation is set up *before* the inventory store is accessed anywhere (which now triggers auto equip and animation update). Allows better tracking of magic VFX for permanent enchantments in InventoryStore.

This commit is contained in:
scrawl 2013-11-14 14:41:10 +01:00
parent 956d8adb99
commit 992a8e9c36
19 changed files with 86 additions and 123 deletions

View file

@ -410,13 +410,16 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mVerboseScripts, *mScriptContext));
// Create game mechanics system
mEnvironment.setMechanicsManager (new MWMechanics::MechanicsManager);
MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;
mEnvironment.setMechanicsManager (mechanics);
// Create dialog system
mEnvironment.setJournal (new MWDialogue::Journal);
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage));
mEnvironment.getWorld()->renderPlayer();
mechanics->buildPlayer();
window->updatePlayer();
if (!mNewGame)
{

View file

@ -254,7 +254,7 @@ namespace MWClass
void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
renderingInterface.getActors().insertNPC(ptr, getInventoryStore(ptr));
renderingInterface.getActors().insertNPC(ptr);
}
void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const

View file

@ -47,8 +47,6 @@ namespace MWGui
, mIngredients (4)
, mSortModel(NULL)
{
mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
getWidget(mCreateButton, "CreateButton");
getWidget(mCancelButton, "CancelButton");
getWidget(mIngredients[0], "Ingredient1");
@ -145,6 +143,8 @@ namespace MWGui
void AlchemyWindow::open()
{
mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
mSortModel = new SortFilterItemModel(model);
mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients);

View file

@ -63,8 +63,6 @@ namespace MWGui
mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected);
mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &InventoryWindow::onBackgroundSelected);
updatePlayer();
mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
@ -75,8 +73,6 @@ namespace MWGui
setCoord(mPositionInventory.left, mPositionInventory.top, mPositionInventory.width, mPositionInventory.height);
onWindowResize(static_cast<MyGUI::Window*>(mMainWidget));
mPreview.setup();
}
void InventoryWindow::updatePlayer()

View file

@ -45,7 +45,6 @@ void ItemView::setModel(ItemModel *model)
{
delete mModel;
mModel = model;
update();
}
void ItemView::initialiseOverride()

View file

@ -51,8 +51,6 @@ namespace MWGui
setCoord(498, 300, 302, 300);
updateSpells();
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SpellWindow::onWindowResize);
}

View file

@ -246,9 +246,6 @@ namespace MWGui
mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat<float>()));
}
unsetSelectedSpell();
unsetSelectedWeapon();
// Set up visibility
updateVisible();
@ -1333,6 +1330,9 @@ namespace MWGui
void WindowManager::updatePlayer()
{
unsetSelectedSpell();
unsetSelectedWeapon();
mInventoryWindow->updatePlayer();
}

View file

@ -167,7 +167,7 @@ namespace MWMechanics
: mUpdatePlayer (true), mClassSelected (false),
mRaceSelected (false)
{
buildPlayer();
//buildPlayer no longer here, needs to be done explicitely after all subsystems are up and running
}
void MechanicsManager::add(const MWWorld::Ptr& ptr)

View file

@ -33,12 +33,12 @@ namespace MWMechanics
Objects mObjects;
Actors mActors;
public:
void buildPlayer();
///< build player according to stored class/race/birthsign information. Will
/// default to the values of the ESM::NPC object, if no explicit information is given.
public:
MechanicsManager();
virtual void add (const MWWorld::Ptr& ptr);

View file

@ -68,13 +68,16 @@ void Actors::insertBegin(const MWWorld::Ptr &ptr)
ptr.getRefData().setBaseNode(insert);
}
void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv)
void Actors::insertNPC(const MWWorld::Ptr& ptr)
{
insertBegin(ptr);
NpcAnimation* anim = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), inv, RV_Actors);
NpcAnimation* anim = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), RV_Actors);
delete mAllActors[ptr];
mAllActors[ptr] = anim;
mRendering->addWaterRippleEmitter (ptr);
// Create CustomData, will do autoEquip and trigger animation parts update
ptr.getClass().getInventoryStore(ptr);
}
void Actors::insertCreature (const MWWorld::Ptr& ptr)
{

View file

@ -39,7 +39,7 @@ namespace MWRender
void setRootNode(Ogre::SceneNode* root);
void insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv);
void insertNPC(const MWWorld::Ptr& ptr);
void insertCreature (const MWWorld::Ptr& ptr);
void insertActivator (const MWWorld::Ptr& ptr);
bool deleteObject (const MWWorld::Ptr& ptr);

View file

@ -70,8 +70,9 @@ namespace MWRender
mNode = renderRoot->createChildSceneNode();
mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter),
mAnimation = new NpcAnimation(mCharacter, mNode,
0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
mAnimation->updateParts();
Ogre::Vector3 scale = mNode->getScale();
mCamera->setPosition(mPosition * scale);
@ -112,10 +113,9 @@ namespace MWRender
{
assert(mAnimation);
delete mAnimation;
mAnimation = 0;
mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter),
mAnimation = new NpcAnimation(mCharacter, mNode,
0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
mAnimation->updateParts();
float scale=1.f;
MWWorld::Class::get(mCharacter).adjustScale(mCharacter, scale);
@ -193,7 +193,7 @@ namespace MWRender
else if(mAnimation->getInfo("torch"))
mAnimation->disable("torch");
mAnimation->updateParts(true);
mAnimation->updateParts();
mAnimation->runAnimation(0.0f);
mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024)));

View file

@ -64,25 +64,11 @@ NpcAnimation::~NpcAnimation()
}
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, ViewMode viewMode)
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int visibilityFlags, ViewMode viewMode)
: Animation(ptr, node),
mStateID(-1),
mTimeToChange(0),
mVisibilityFlags(visibilityFlags),
mRobe(inv.end()),
mHelmet(inv.end()),
mShirt(inv.end()),
mCuirass(inv.end()),
mGreaves(inv.end()),
mPauldronL(inv.end()),
mPauldronR(inv.end()),
mBoots(inv.end()),
mPants(inv.end()),
mGloveL(inv.end()),
mGloveR(inv.end()),
mSkirtIter(inv.end()),
mWeapon(inv.end()),
mShield(inv.end()),
mViewMode(viewMode),
mShowWeapons(false),
mFirstPersonOffset(0.f, 0.f, 0.f)
@ -94,8 +80,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
mPartslots[i] = -1; //each slot is empty
mPartPriorities[i] = 0;
}
updateNpcBase();
}
void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
@ -173,56 +157,50 @@ void NpcAnimation::updateNpcBase()
for(size_t i = 0;i < ESM::PRT_Count;i++)
removeIndividualPart((ESM::PartReferenceType)i);
updateParts(true);
updateParts();
}
void NpcAnimation::updateParts(bool forceupdate)
void NpcAnimation::updateParts()
{
if (!mSkelBase)
{
// First update?
updateNpcBase();
return;
}
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
static const struct {
MWWorld::ContainerStoreIterator NpcAnimation::*mPart;
int mSlot;
int mBasePriority;
} slotlist[] = {
// FIXME: Priority is based on the number of reserved slots. There should be a better way.
{ &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, 12 },
{ &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, 3 },
{ &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, 0 },
{ &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, 0 },
{ &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, 0 },
{ &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, 0 },
{ &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, 0 },
{ &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, 0 },
{ &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, 0 },
{ &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, 0 },
{ &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, 0 },
{ &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, 0 },
{ &NpcAnimation::mShield, MWWorld::InventoryStore::Slot_CarriedLeft, 0 },
{ &NpcAnimation::mWeapon, MWWorld::InventoryStore::Slot_CarriedRight, 0 }
{ MWWorld::InventoryStore::Slot_Robe, 12 },
{ 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]);
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
for(size_t i = 0;!forceupdate && i < slotlistsize;i++)
{
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot);
if(this->*slotlist[i].mPart != iter)
{
forceupdate = true;
break;
}
}
if(!forceupdate)
return;
for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++)
{
MWWorld::ContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot);
this->*slotlist[i].mPart = store;
removePartGroup(slotlist[i].mSlot);
if(this->*slotlist[i].mPart == inv.end())
if(store == inv.end())
continue;
if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet)
@ -439,13 +417,6 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in
Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
{
if(mTimeToChange <= 0.0f)
{
mTimeToChange = 0.2f;
updateParts();
}
mTimeToChange -= timepassed;
Ogre::Vector3 ret = Animation::runAnimation(timepassed);
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
@ -599,11 +570,10 @@ void NpcAnimation::showWeapons(bool showWeapon)
if(showWeapon)
{
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(mWeapon != inv.end()) // special case for weapons
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end()) // special case for weapons
{
MWWorld::Ptr weapon = *mWeapon;
std::string mesh = MWWorld::Class::get(weapon).getModel(weapon);
std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon);
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh);
}
}

View file

@ -43,22 +43,6 @@ private:
ViewMode mViewMode;
bool mShowWeapons;
float mTimeToChange;
MWWorld::ContainerStoreIterator mRobe;
MWWorld::ContainerStoreIterator mHelmet;
MWWorld::ContainerStoreIterator mShirt;
MWWorld::ContainerStoreIterator mCuirass;
MWWorld::ContainerStoreIterator mGreaves;
MWWorld::ContainerStoreIterator mPauldronL;
MWWorld::ContainerStoreIterator mPauldronR;
MWWorld::ContainerStoreIterator mBoots;
MWWorld::ContainerStoreIterator mPants;
MWWorld::ContainerStoreIterator mGloveL;
MWWorld::ContainerStoreIterator mGloveR;
MWWorld::ContainerStoreIterator mSkirtIter;
MWWorld::ContainerStoreIterator mWeapon;
MWWorld::ContainerStoreIterator mShield;
int mVisibilityFlags;
int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty
@ -78,8 +62,7 @@ private:
void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts);
public:
NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node,
MWWorld::InventoryStore& inv, int visibilityFlags,
NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int visibilityFlags,
ViewMode viewMode=VM_Normal);
virtual ~NpcAnimation();
@ -89,7 +72,7 @@ public:
void setViewMode(ViewMode viewMode);
void updateParts(bool forceupdate = false);
void updateParts();
/// \brief Applies a translation to the arms and hands.
/// This may be called multiple times before the animation

View file

@ -336,6 +336,7 @@ void RenderingManager::updateAnimParts(const MWWorld::Ptr& ptr)
else if(MWWorld::Class::get(ptr).isActor())
anim = dynamic_cast<NpcAnimation*>(mActors.getAnimation(ptr));
assert(anim);
if(anim)
anim->updateParts();
}
@ -926,18 +927,17 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr)
{
if(!mPlayerAnimation)
{
mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(),
MWWorld::Class::get(ptr).getInventoryStore(ptr),
RV_Actors);
mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), RV_Actors);
}
else
{
// Reconstruct the NpcAnimation in-place
mPlayerAnimation->~NpcAnimation();
new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(),
MWWorld::Class::get(ptr).getInventoryStore(ptr),
RV_Actors);
new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(), RV_Actors);
}
// Ensure CustomData -> autoEquip -> animation update
ptr.getClass().getInventoryStore(ptr);
mCamera->setAnimation(mPlayerAnimation);
mWater->removeEmitter(ptr);
mWater->addEmitter(ptr);

View file

@ -45,6 +45,7 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_)
MWWorld::InventoryStore::InventoryStore()
: mSelectedEnchantItem(end())
, mUpdatesEnabled (true)
, mFirstAutoEquip(true)
{
initSlots (mSlots);
}
@ -54,6 +55,7 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
, mSelectedEnchantItem(end())
{
mMagicEffects = store.mMagicEffects;
mFirstAutoEquip = store.mFirstAutoEquip;
mSelectedEnchantItem = store.mSelectedEnchantItem;
mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes;
copySlots (store);
@ -62,6 +64,7 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store)
{
mMagicEffects = store.mMagicEffects;
mFirstAutoEquip = store.mFirstAutoEquip;
mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes;
ContainerStore::operator= (store);
mSlots.clear();
@ -256,6 +259,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc)
updateMagicEffects(npc);
flagAsModified();
}
mFirstAutoEquip = false;
}
const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() const
@ -308,8 +312,10 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
// so it doesn't really matter if both items will get the same magnitude. *Extreme* edge case.
mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID] = random;
// TODO: What do we do if no animation yet?
if (MWBase::Environment::get().getWorld()->getAnimation(actor))
// During first auto equip, we don't play any sounds.
// Basically we don't want sounds when the actor is first loaded,
// the items should appear as if they'd always been equipped.
if (!mFirstAutoEquip)
{
// Only the sound of the first effect plays
if (effectIt == enchantment.mEffects.mList.begin())
@ -324,13 +330,15 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
else
sndMgr->playSound3D(actor, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f);
}
}
if (!magicEffect->mHit.empty())
{
const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
if (!magicEffect->mHit.empty())
{
const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
// Similar as above, we don't want particles during first autoequip either, unless they're continuous.
if (!mFirstAutoEquip || loop)
MWBase::Environment::get().getWorld()->getAnimation(actor)->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
}
}
}

View file

@ -49,6 +49,8 @@ namespace MWWorld
// This is disabled during autoequip to avoid excessive updates
bool mUpdatesEnabled;
bool mFirstAutoEquip;
// Vanilla allows permanent effects with a random magnitude, so it needs to be stored here.
// We also need this to only play sounds and particle effects when the item is equipped, rather than on every update.
typedef std::map<std::string, std::vector<float> > TEffectMagnitudes;

View file

@ -118,8 +118,6 @@ namespace MWWorld
void Scene::loadCell (Ptr::CellStore *cell, Loading::Listener* loadingListener)
{
// register local scripts
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell);
if(result.second)
@ -157,6 +155,10 @@ namespace MWWorld
mRendering.requestMap(cell);
mRendering.configureAmbient(*cell);
}
// register local scripts
// ??? Should this go into the above if block ???
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
}
void Scene::playerCellChange(MWWorld::CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos)

View file

@ -2239,7 +2239,6 @@ namespace MWWorld
void World::updateAnimParts(const Ptr& actor)
{
if (actor.mCell && actor.mCell == mWorldScene->getCurrentCell())
mRendering->updateAnimParts(actor);
mRendering->updateAnimParts(actor);
}
}