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.

actorid
scrawl 11 years ago
parent 956d8adb99
commit 992a8e9c36

@ -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)
{

@ -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

@ -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);

@ -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()

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

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

@ -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();
}

@ -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)

@ -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);

@ -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)
{

@ -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);

@ -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)));

@ -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);
}
}

@ -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

@ -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);

@ -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, "");
}
}
}

@ -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;

@ -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)

@ -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);
}
}

Loading…
Cancel
Save