From 0be1ac13436d418a64ca25327685fae09295247f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 28 Aug 2014 21:37:18 +0200 Subject: [PATCH 01/12] Enable casting of NoMagnitude effects in AI (e.g. summoned creatures) --- apps/openmw/mwmechanics/aicombataction.cpp | 25 +++++----------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index c10c982b8..0bdf3772e 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -178,7 +178,7 @@ namespace MWMechanics { // NOTE: target may be empty - float baseRating = 1; + float rating = 1; switch (effect.mEffectID) { case ESM::MagicEffect::Soultrap: @@ -231,24 +231,11 @@ namespace MWMechanics } break; - // Give a small boost to all direct damage effects. This is combat, after all! - case ESM::MagicEffect::FireDamage: - case ESM::MagicEffect::ShockDamage: - case ESM::MagicEffect::FrostDamage: - case ESM::MagicEffect::Poison: - case ESM::MagicEffect::AbsorbHealth: - case ESM::MagicEffect::DamageHealth: - baseRating *= 4; - break; - - case ESM::MagicEffect::Paralyze: // *Evil laughter* - baseRating *= 5; - break; - // TODO: rate these effects very high if we are currently suffering from negative effects that could be cured case ESM::MagicEffect::Dispel: case ESM::MagicEffect::CureParalyzation: case ESM::MagicEffect::CurePoison: + return 0.f; break; default: @@ -261,12 +248,10 @@ namespace MWMechanics const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - baseRating *= magicEffect->mData.mBaseCost; + rating *= magicEffect->mData.mBaseCost; - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - return 0.f; // No clue how useful this would be; will need special cases for each effect - - float rating = baseRating * (effect.mMagnMin + effect.mMagnMax)/2.f; + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + rating *= (effect.mMagnMin + effect.mMagnMax)/2.f; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) rating *= effect.mDuration; From a62e40f4ed3f8d9e3946dc51bdebc8a983907ce5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 29 Aug 2014 17:58:32 +0200 Subject: [PATCH 02/12] Add situational AI use of Feather, Dispel and Cure effects --- apps/openmw/mwmechanics/aicombataction.cpp | 71 +++++++++++++++++++--- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 0bdf3772e..b61e5a121 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -59,6 +59,29 @@ void suggestCombatRange(int rangeTypes, float& rangeAttack, float& rangeFollow) } } +int numEffectsToCure (const MWWorld::Ptr& actor, int effectFilter=-1) +{ + int toCure=0; + const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells(); + for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it) + { + const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second; + for (std::vector::const_iterator effectIt = params.mEffects.begin(); + effectIt != params.mEffects.end(); ++effectIt) + { + int effectId = effectIt->mEffectId; + if (effectFilter != -1 && effectId != effectFilter) + continue; + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectId); + if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful + && effectIt->mDuration > 3 // Don't attempt to cure if effect runs out shortly anyway + ) + ++toCure; + } + } + return toCure; +} + } namespace MWMechanics @@ -184,14 +207,18 @@ namespace MWMechanics case ESM::MagicEffect::Soultrap: case ESM::MagicEffect::AlmsiviIntervention: case ESM::MagicEffect::DivineIntervention: - case ESM::MagicEffect::CalmCreature: case ESM::MagicEffect::CalmHumanoid: + case ESM::MagicEffect::CalmCreature: + case ESM::MagicEffect::FrenzyHumanoid: + case ESM::MagicEffect::FrenzyCreature: + case ESM::MagicEffect::DemoralizeHumanoid: + case ESM::MagicEffect::DemoralizeCreature: + case ESM::MagicEffect::RallyHumanoid: + case ESM::MagicEffect::RallyCreature: case ESM::MagicEffect::Charm: case ESM::MagicEffect::DetectAnimal: case ESM::MagicEffect::DetectEnchantment: case ESM::MagicEffect::DetectKey: - case ESM::MagicEffect::FrenzyCreature: - case ESM::MagicEffect::FrenzyHumanoid: case ESM::MagicEffect::Telekinesis: case ESM::MagicEffect::Mark: case ESM::MagicEffect::Recall: @@ -204,16 +231,39 @@ namespace MWMechanics case ESM::MagicEffect::Lock: case ESM::MagicEffect::Open: case ESM::MagicEffect::TurnUndead: + case ESM::MagicEffect::WeaknessToCommonDisease: + case ESM::MagicEffect::WeaknessToBlightDisease: + case ESM::MagicEffect::WeaknessToCorprusDisease: + case ESM::MagicEffect::CureCommonDisease: + case ESM::MagicEffect::CureBlightDisease: + case ESM::MagicEffect::CureCorprusDisease: + case ESM::MagicEffect::Invisibility: return 0.f; case ESM::MagicEffect::Feather: - return 0.f; // TODO: check if target is overencumbered + if (actor.getClass().getEncumbrance(actor) - actor.getClass().getCapacity(actor) >= 0) + return 100.f; + else + return 0.f; case ESM::MagicEffect::Levitate: return 0.f; // AI isn't designed to take advantage of this, and could be perceived as unfair anyway - // TODO: check if Beast race (can't wear boots or helm) - /* case ESM::MagicEffect::BoundBoots: case ESM::MagicEffect::BoundHelm: - */ + if (actor.getClass().isNpc()) + { + // Beast races can't wear helmets or boots + std::string raceid = actor.get()->mBase->mRace; + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(raceid); + if (race->mData.mFlags & ESM::Race::Beast) + return 0.f; + } + // Intended fall-through + // Creatures can not wear armor + case ESM::MagicEffect::BoundCuirass: + case ESM::MagicEffect::BoundGloves: + if (!actor.getClass().isNpc()) + return 0.f; + break; + case ESM::MagicEffect::RestoreHealth: case ESM::MagicEffect::RestoreMagicka: case ESM::MagicEffect::RestoreFatigue: @@ -231,12 +281,13 @@ namespace MWMechanics } break; - // TODO: rate these effects very high if we are currently suffering from negative effects that could be cured + // Prefer Cure effects over Dispel, because Dispel also removes positive effects case ESM::MagicEffect::Dispel: + return 1000.f * numEffectsToCure(actor); case ESM::MagicEffect::CureParalyzation: + return 1001.f * numEffectsToCure(actor, ESM::MagicEffect::Paralyze); case ESM::MagicEffect::CurePoison: - return 0.f; - break; + return 1001.f * numEffectsToCure(actor, ESM::MagicEffect::Poison); default: break; From 58945c557a4ca65e71e2c773181d41cc6614ff79 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 30 Aug 2014 02:45:35 +0200 Subject: [PATCH 03/12] Add sound for casting enchanted item with insufficent charge (Fixes #1857) --- apps/openmw/mwmechanics/spellcasting.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 6563d0e62..a1f0d994a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -684,9 +684,23 @@ namespace MWMechanics if (item.getCellRef().getEnchantmentCharge() < castCost) { - // TODO: Should there be a sound here? if (mCaster.getRefData().getHandle() == "player") MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); + + // Failure sound + int school = 0; + for (std::vector::const_iterator effectIt (enchantment->mEffects.mList.begin()); + effectIt!=enchantment->mEffects.mList.end(); ++effectIt) + { + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); + school = magicEffect->mData.mSchool; + break; + } + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f); return false; } // Reduce charge From 267cf4e140f95efde49afe809e19b5510aa2f0dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 30 Aug 2014 17:55:35 +0200 Subject: [PATCH 04/12] Implement ResetActors script instruction (Fixes #1859) --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwscript/docs/vmformat.txt | 3 ++- .../mwscript/transformationextensions.cpp | 10 +++++++ apps/openmw/mwworld/worldimp.cpp | 27 +++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 +++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 1 + 7 files changed, 48 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 02e143286..f07bb3eb9 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -536,6 +536,9 @@ namespace MWBase /// @see MWWorld::WeatherManager::getStormDirection virtual Ogre::Vector3 getStormDirection() const = 0; + + /// Resets all actors in the current active cells to their original location within that cell. + virtual void resetActors() = 0; }; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 7397355e3..626332564 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -431,5 +431,6 @@ op 0x2000294-0x20002ab: SetMagicEffect op 0x20002ac-0x20002c3: SetMagicEffect, explicit op 0x20002c4-0x20002db: ModMagicEffect op 0x20002dc-0x20002f3: ModMagicEffect, explicit +op 0x20002f4: ResetActors -opcodes 0x20002f4-0x3ffffff unused +opcodes 0x20002f5-0x3ffffff unused diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 3355e705f..42f44512a 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -719,6 +719,15 @@ namespace MWScript } }; + class OpResetActors : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::Environment::get().getWorld()->resetActors(); + } + }; void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -759,6 +768,7 @@ namespace MWScript interpreter.installSegment5(Compiler::Transformation::opcodeMoveWorldExplicit,new OpMoveWorld); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle); + interpreter.installSegment5(Compiler::Transformation::opcodeResetActors, new OpResetActors); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f19a40313..c1147fa03 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2989,4 +2989,31 @@ namespace MWWorld if (!interpreterContext.hasActivationBeenHandled()) interpreterContext.executeActivation(object, actor); } + + struct ResetActorsFunctor + { + bool operator() (Ptr ptr) + { + // Can't reset actors that were moved to a different cell, because we don't know what cell they came from. + // This could be fixed once we properly track actor cell changes, but may not be desirable behaviour anyhow. + if (ptr.getClass().isActor() && ptr.getCellRef().getRefNum().mContentFile != -1) + { + const ESM::Position& origPos = ptr.getCellRef().getPosition(); + MWBase::Environment::get().getWorld()->moveObject(ptr, origPos.pos[0], origPos.pos[1], origPos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(ptr, origPos.rot[0], origPos.rot[1], origPos.rot[2]); + ptr.getClass().adjustPosition(ptr, false); + } + return true; + } + }; + void World::resetActors() + { + for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); + iter!=mWorldScene->getActiveCells().end(); ++iter) + { + CellStore* cellstore = *iter; + ResetActorsFunctor functor; + cellstore->forEach(functor); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4dade0f21..eddd056e0 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -611,6 +611,9 @@ namespace MWWorld /// @see MWWorld::WeatherManager::getStormDirection virtual Ogre::Vector3 getStormDirection() const; + + /// Resets all actors in the current active cells to their original location within that cell. + virtual void resetActors(); }; } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index a2dc97a1a..e12ab65eb 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -527,6 +527,8 @@ namespace Compiler extensions.registerInstruction("move","cf",opcodeMove,opcodeMoveExplicit); extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit); extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); + extensions.registerInstruction("resetactors","",opcodeResetActors); + extensions.registerInstruction("ra","",opcodeResetActors); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index b37a78d62..440475773 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -484,6 +484,7 @@ namespace Compiler const int opcodeMoveExplicit = 0x2000207; const int opcodeMoveWorld = 0x2000208; const int opcodeMoveWorldExplicit = 0x2000209; + const int opcodeResetActors = 0x20002f4; } namespace User From fde6cad24b4d60f3c73b0ce52f4bf44d3e2ea615 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 30 Aug 2014 18:25:13 +0200 Subject: [PATCH 05/12] Reset RefNum when copying a reference to a new cell --- apps/openmw/mwworld/class.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index d241278d1..59fc86d0d 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -358,7 +358,7 @@ namespace MWWorld Class::copyToCell(const Ptr &ptr, CellStore &cell) const { Ptr newPtr = copyToCellImpl(ptr, cell); - + newPtr.getCellRef().unsetRefNum(); // This RefNum is only valid within the original cell of the reference return newPtr; } From 180a831e96d5811505ca6c9533e9bfebce380a73 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 30 Aug 2014 18:37:51 +0200 Subject: [PATCH 06/12] Fix typo in combat distance --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 3785ba9ae..88c616005 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -280,7 +280,7 @@ namespace MWMechanics || actor1.getClass().getCreatureStats(actor1).isDead()) return; - const ESM::Position& actor1Pos = actor2.getRefData().getPosition(); + const ESM::Position& actor1Pos = actor1.getRefData().getPosition(); const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); float sqrDist = Ogre::Vector3(actor1Pos.pos).squaredDistance(Ogre::Vector3(actor2Pos.pos)); if (sqrDist > 7168*7168) From 9d6ca9066f7f5a0bff9001513361b7f54968a7be Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 30 Aug 2014 18:48:28 +0200 Subject: [PATCH 07/12] Fix summoned creatures not following for non-player summoners --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 88c616005..75094861e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -756,7 +756,7 @@ namespace MWMechanics MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); // Make the summoned creature follow its master and help in fights - AiFollow package(ptr.getRefData().getHandle()); + AiFollow package(ptr.getCellRef().getRefId()); summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); int creatureActorId = summonedCreatureStats.getActorId(); From 59720b7dc67e7eaf2688979c971ba4e68284d877 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 1 Sep 2014 02:04:44 +0200 Subject: [PATCH 08/12] Implement MagicItems flag for vendors (Fixes #1861) --- apps/openmw/mwclass/armor.cpp | 3 ++- apps/openmw/mwclass/book.cpp | 3 ++- apps/openmw/mwclass/clothing.cpp | 3 ++- apps/openmw/mwclass/weapon.cpp | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index f842528d3..97f9211d9 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -380,7 +380,8 @@ namespace MWClass bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const { - return npcServices & ESM::NPC::Armor; + return (npcServices & ESM::NPC::Armor) + || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Armor::getWeight(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 5e9b86af0..51d47e721 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -202,7 +202,8 @@ namespace MWClass bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const { - return npcServices & ESM::NPC::Books; + return (npcServices & ESM::NPC::Books) + || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Book::getWeight(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 3e27b7584..009878350 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -292,7 +292,8 @@ namespace MWClass bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const { - return npcServices & ESM::NPC::Clothing; + return (npcServices & ESM::NPC::Clothing) + || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Clothing::getWeight(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 6f75095ff..f958302cf 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -434,7 +434,8 @@ namespace MWClass bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const { - return npcServices & ESM::NPC::Weapon; + return (npcServices & ESM::NPC::Weapon) + || ((npcServices & ESM::NPC::MagicItems) && !getEnchantment(item).empty()); } float Weapon::getWeight(const MWWorld::Ptr &ptr) const From 9caff1d8001df3eec4e445059fae3813d23b4fb2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 1 Sep 2014 02:21:54 +0200 Subject: [PATCH 09/12] Don't say Idle voices when player is not in LOS (Fixes #1436) --- apps/openmw/mwmechanics/aiwander.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 6b73c9eb4..2f51b6467 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -406,7 +406,11 @@ namespace MWMechanics static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() .get().find("fVoiceIdleOdds")->getFloat(); - if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500) + // Only say Idle voices when player is in LOS + // A bit counterintuitive, likely vanilla did this to reduce the appearance of + // voices going through walls? + if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500 + && MWBase::Environment::get().getWorld()->getLOS(player, actor)) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } From 9809748eba2a0514019f99e268d044c5f23ebc8a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 2 Sep 2014 03:04:38 +0200 Subject: [PATCH 10/12] Store NIF keys mapped by time and use lower_bound for searches --- components/nif/data.hpp | 20 ++++----- components/nif/nifkey.hpp | 39 ++++++++--------- components/nifogre/controller.hpp | 59 +++++++++++++------------- components/nifogre/ogrenifloader.cpp | 62 ++++++++++++++-------------- 4 files changed, 93 insertions(+), 87 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 7fd905f00..34ef3a34e 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -210,7 +210,7 @@ public: class NiPosData : public Record { public: - Vector3KeyList mKeyList; + Vector3KeyMap mKeyList; void read(NIFStream *nif) { @@ -221,7 +221,7 @@ public: class NiUVData : public Record { public: - FloatKeyList mKeyList[4]; + FloatKeyMap mKeyList[4]; void read(NIFStream *nif) { @@ -233,7 +233,7 @@ public: class NiFloatData : public Record { public: - FloatKeyList mKeyList; + FloatKeyMap mKeyList; void read(NIFStream *nif) { @@ -283,11 +283,11 @@ public: class NiColorData : public Record { public: - Vector4KeyList mKeyList; + Vector4KeyMap mKeyMap; void read(NIFStream *nif) { - mKeyList.read(nif); + mKeyMap.read(nif); } }; @@ -388,7 +388,7 @@ public: struct NiMorphData : public Record { struct MorphData { - FloatKeyList mData; + FloatKeyMap mData; std::vector mVertices; }; std::vector mMorphs; @@ -411,11 +411,11 @@ struct NiMorphData : public Record struct NiKeyframeData : public Record { - QuaternionKeyList mRotations; + QuaternionKeyMap mRotations; //\FIXME mXYZ_Keys are read, but not used. - FloatKeyList mXYZ_Keys; - Vector3KeyList mTranslations; - FloatKeyList mScales; + FloatKeyMap mXYZ_Keys; + Vector3KeyMap mTranslations; + FloatKeyMap mScales; void read(NIFStream *nif) { diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index 403499e7f..b0db80914 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -12,7 +12,6 @@ namespace Nif template struct KeyT { - float mTime; T mValue; T mForwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList T mBackwardValue; // Only for Quadratic interpolation, and never for QuaternionKeyList @@ -26,8 +25,8 @@ typedef KeyT Vector4Key; typedef KeyT QuaternionKey; template -struct KeyListT { - typedef std::vector< KeyT > VecType; +struct KeyMapT { + typedef std::map< float, KeyT > MapType; static const unsigned int sLinearInterpolation = 1; static const unsigned int sQuadraticInterpolation = 2; @@ -35,9 +34,9 @@ struct KeyListT { static const unsigned int sXYZInterpolation = 4; unsigned int mInterpolationType; - VecType mKeys; + MapType mKeys; - KeyListT() : mInterpolationType(sLinearInterpolation) {} + KeyMapT() : mInterpolationType(sLinearInterpolation) {} //Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html) void read(NIFStream *nif, bool force=false) @@ -63,24 +62,27 @@ struct KeyListT { { for(size_t i = 0;i < count;i++) { - readTimeAndValue(nifReference, key); - mKeys.push_back(key); + float time = nif->getFloat(); + readValue(nifReference, key); + mKeys[time] = key; } } else if(mInterpolationType == sQuadraticInterpolation) { for(size_t i = 0;i < count;i++) { + float time = nif->getFloat(); readQuadratic(nifReference, key); - mKeys.push_back(key); + mKeys[time] = key; } } else if(mInterpolationType == sTBCInterpolation) { for(size_t i = 0;i < count;i++) { + float time = nif->getFloat(); readTBC(nifReference, key); - mKeys.push_back(key); + mKeys[time] = key; } } //XYZ keys aren't actually read here. @@ -104,37 +106,36 @@ struct KeyListT { } private: - static void readTimeAndValue(NIFStream &nif, KeyT &key) + static void readValue(NIFStream &nif, KeyT &key) { - key.mTime = nif.getFloat(); key.mValue = (nif.*getValue)(); } static void readQuadratic(NIFStream &nif, KeyT &key) { - readTimeAndValue(nif, key); + readValue(nif, key); } template static void readQuadratic(NIFStream &nif, KeyT &key) { - readTimeAndValue(nif, key); + readValue(nif, key); key.mForwardValue = (nif.*getValue)(); key.mBackwardValue = (nif.*getValue)(); } static void readTBC(NIFStream &nif, KeyT &key) { - readTimeAndValue(nif, key); + readValue(nif, key); key.mTension = nif.getFloat(); key.mBias = nif.getFloat(); key.mContinuity = nif.getFloat(); } }; -typedef KeyListT FloatKeyList; -typedef KeyListT Vector3KeyList; -typedef KeyListT Vector4KeyList; -typedef KeyListT QuaternionKeyList; +typedef KeyMapT FloatKeyMap; +typedef KeyMapT Vector3KeyMap; +typedef KeyMapT Vector4KeyMap; +typedef KeyMapT QuaternionKeyMap; } // Namespace -#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP \ No newline at end of file +#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP diff --git a/components/nifogre/controller.hpp b/components/nifogre/controller.hpp index 317447d95..f4d7ac29f 100644 --- a/components/nifogre/controller.hpp +++ b/components/nifogre/controller.hpp @@ -2,6 +2,7 @@ #define COMPONENTS_NIFOGRE_CONTROLLER_H #include +#include #include namespace NifOgre @@ -10,53 +11,55 @@ namespace NifOgre class ValueInterpolator { protected: - float interpKey(const Nif::FloatKeyList::VecType &keys, float time, float def=0.f) const + float interpKey(const Nif::FloatKeyMap::MapType &keys, float time, float def=0.f) const { if (keys.size() == 0) return def; - if(time <= keys.front().mTime) - return keys.front().mValue; + if(time <= keys.begin()->first) + return keys.begin()->second.mValue; - const Nif::FloatKey* keyArray = keys.data(); - size_t size = keys.size(); - - for (size_t i = 1; i < size; ++i) + Nif::FloatKeyMap::MapType::const_iterator it = keys.lower_bound(time); + if (it != keys.end()) { - const Nif::FloatKey* aKey = &keyArray[i]; + float aTime = it->first; + const Nif::FloatKey* aKey = &it->second; - if(aKey->mTime < time) - continue; + assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function - const Nif::FloatKey* aLastKey = &keyArray[i-1]; - float a = (time - aLastKey->mTime) / (aKey->mTime - aLastKey->mTime); + Nif::FloatKeyMap::MapType::const_iterator last = --it; + float aLastTime = last->first; + const Nif::FloatKey* aLastKey = &last->second; + + float a = (time - aLastTime) / (aTime - aLastTime); return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a); } - - return keys.back().mValue; + else + return (--keys.end())->second.mValue; } - Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time) const + Ogre::Vector3 interpKey(const Nif::Vector3KeyMap::MapType &keys, float time) const { - if(time <= keys.front().mTime) - return keys.front().mValue; + if(time <= keys.begin()->first) + return keys.begin()->second.mValue; - const Nif::Vector3Key* keyArray = keys.data(); - size_t size = keys.size(); - - for (size_t i = 1; i < size; ++i) + Nif::Vector3KeyMap::MapType::const_iterator it = keys.lower_bound(time); + if (it != keys.end()) { - const Nif::Vector3Key* aKey = &keyArray[i]; + float aTime = it->first; + const Nif::Vector3Key* aKey = &it->second; - if(aKey->mTime < time) - continue; + assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function - const Nif::Vector3Key* aLastKey = &keyArray[i-1]; - float a = (time - aLastKey->mTime) / (aKey->mTime - aLastKey->mTime); + Nif::Vector3KeyMap::MapType::const_iterator last = --it; + float aLastTime = last->first; + const Nif::Vector3Key* aLastKey = &last->second; + + float a = (time - aLastTime) / (aTime - aLastTime); return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a); } - - return keys.back().mValue; + else + return (--keys.end())->second.mValue; } }; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 83716764c..531044c26 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -237,7 +237,7 @@ public: { private: Ogre::MovableObject* mMovable; - Nif::FloatKeyList mData; + Nif::FloatKeyMap mData; MaterialControllerManager* mMaterialControllerMgr; public: @@ -284,7 +284,7 @@ public: { private: Ogre::MovableObject* mMovable; - Nif::Vector3KeyList mData; + Nif::Vector3KeyMap mData; MaterialControllerManager* mMaterialControllerMgr; public: @@ -409,34 +409,35 @@ public: class Value : public NodeTargetValue, public ValueInterpolator { private: - const Nif::QuaternionKeyList* mRotations; - const Nif::Vector3KeyList* mTranslations; - const Nif::FloatKeyList* mScales; + const Nif::QuaternionKeyMap* mRotations; + const Nif::Vector3KeyMap* mTranslations; + const Nif::FloatKeyMap* mScales; Nif::NIFFilePtr mNif; // Hold a SharedPtr to make sure key lists stay valid using ValueInterpolator::interpKey; - static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time) + static Ogre::Quaternion interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time) { - if(time <= keys.front().mTime) - return keys.front().mValue; + if(time <= keys.begin()->first) + return keys.begin()->second.mValue; - const Nif::QuaternionKey* keyArray = keys.data(); - size_t size = keys.size(); - - for (size_t i = 1; i < size; ++i) + Nif::QuaternionKeyMap::MapType::const_iterator it = keys.lower_bound(time); + if (it != keys.end()) { - const Nif::QuaternionKey* aKey = &keyArray[i]; + float aTime = it->first; + const Nif::QuaternionKey* aKey = &it->second; - if(aKey->mTime < time) - continue; + assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function - const Nif::QuaternionKey* aLastKey = &keyArray[i-1]; - float a = (time - aLastKey->mTime) / (aKey->mTime - aLastKey->mTime); + Nif::QuaternionKeyMap::MapType::const_iterator last = --it; + float aLastTime = last->first; + const Nif::QuaternionKey* aLastKey = &last->second; + + float a = (time - aLastTime) / (aTime - aLastTime); return Ogre::Quaternion::nlerp(a, aLastKey->mValue, aKey->mValue); } - - return keys.back().mValue; + else + return (--keys.end())->second.mValue; } public: @@ -497,10 +498,10 @@ public: { private: Ogre::MovableObject* mMovable; - Nif::FloatKeyList mUTrans; - Nif::FloatKeyList mVTrans; - Nif::FloatKeyList mUScale; - Nif::FloatKeyList mVScale; + Nif::FloatKeyMap mUTrans; + Nif::FloatKeyMap mVTrans; + Nif::FloatKeyMap mUScale; + Nif::FloatKeyMap mVScale; MaterialControllerManager* mMaterialControllerMgr; public: @@ -839,18 +840,19 @@ class NIFObjectLoader const Nif::NiColorData *clrdata = cl->data.getPtr(); Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator"); - size_t num_colors = std::min(6, clrdata->mKeyList.mKeys.size()); - for(size_t i = 0;i < num_colors;i++) + size_t num_colors = std::min(6, clrdata->mKeyMap.mKeys.size()); + unsigned int i=0; + for (Nif::Vector4KeyMap::MapType::const_iterator it = clrdata->mKeyMap.mKeys.begin(); it != clrdata->mKeyMap.mKeys.end() && i < num_colors; ++it,++i) { Ogre::ColourValue color; - color.r = clrdata->mKeyList.mKeys[i].mValue[0]; - color.g = clrdata->mKeyList.mKeys[i].mValue[1]; - color.b = clrdata->mKeyList.mKeys[i].mValue[2]; - color.a = clrdata->mKeyList.mKeys[i].mValue[3]; + color.r = it->second.mValue[0]; + color.g = it->second.mValue[1]; + color.b = it->second.mValue[2]; + color.a = it->second.mValue[3]; affector->setParameter("colour"+Ogre::StringConverter::toString(i), Ogre::StringConverter::toString(color)); affector->setParameter("time"+Ogre::StringConverter::toString(i), - Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime)); + Ogre::StringConverter::toString(it->first)); } } else if(e->recType == Nif::RC_NiParticleRotation) From 0eb949c8a732b775bfcbde4659f318a0035b9f24 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 2 Sep 2014 03:41:57 +0200 Subject: [PATCH 11/12] Don't update ripples when they are not used --- apps/openmw/mwrender/water.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 2cbc4462c..752da30df 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -384,10 +384,11 @@ void Water::update(float dt, Ogre::Vector3 player) void Water::frameStarted(float dt) { - mSimulation->update(dt, mPlayer); - if (mReflection) + { + mSimulation->update(dt, mPlayer); mReflection->update(); + } } void Water::applyRTT() From 819234cdfeb1b9cefebd12429aa89ab6827619c5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 2 Sep 2014 04:53:24 +0200 Subject: [PATCH 12/12] Use rbegin() instead of --end() --- components/nifogre/controller.hpp | 4 ++-- components/nifogre/ogrenifloader.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/nifogre/controller.hpp b/components/nifogre/controller.hpp index f4d7ac29f..aa60482e3 100644 --- a/components/nifogre/controller.hpp +++ b/components/nifogre/controller.hpp @@ -35,7 +35,7 @@ namespace NifOgre return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a); } else - return (--keys.end())->second.mValue; + return keys.rbegin()->second.mValue; } Ogre::Vector3 interpKey(const Nif::Vector3KeyMap::MapType &keys, float time) const @@ -59,7 +59,7 @@ namespace NifOgre return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a); } else - return (--keys.end())->second.mValue; + return keys.rbegin()->second.mValue; } }; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 531044c26..910b8b352 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -437,7 +437,7 @@ public: return Ogre::Quaternion::nlerp(a, aLastKey->mValue, aKey->mValue); } else - return (--keys.end())->second.mValue; + return keys.rbegin()->second.mValue; } public: