From 2ce8323a422776f957067fde24ebe40317f17d11 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 16:34:00 +0200 Subject: [PATCH 01/33] Fix getDistance not detecting references in inactive cells properly --- apps/openmw/mwscript/interpretercontext.cpp | 12 ++++++++++-- apps/openmw/mwscript/interpretercontext.hpp | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index ebe88c3a4..028f83a61 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -378,12 +378,20 @@ namespace MWScript float InterpreterContext::getDistance (const std::string& name, const std::string& id) const { - const MWWorld::Ptr ref2 = getReference (id, false, false); + // NOTE: id may be empty, indicating an implicit reference + + MWWorld::Ptr ref2; + + if (id.empty()) + ref2 = getReference("", true, true); + else + ref2 = MWBase::Environment::get().getWorld()->searchPtr(id, true); + // If either actor is in a non-active cell, return a large value (just like vanilla) if (ref2.isEmpty()) return std::numeric_limits().max(); - const MWWorld::Ptr ref = getReference (name, false, false); + const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->searchPtr(name, true); if (ref.isEmpty()) return std::numeric_limits().max(); diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index fdf9d6424..99026392e 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -118,6 +118,7 @@ namespace MWScript virtual void stopScript (const std::string& name); virtual float getDistance (const std::string& name, const std::string& id = "") const; + ///< @note if \a id is empty, assumes an implicit reference bool hasBeenActivated (const MWWorld::Ptr& ptr); ///< \attention Calling this function for the right reference will mark the action as From d11a5e19f79b9ed3cf0e49cde5b1b063a9155fb9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 17:14:35 +0200 Subject: [PATCH 02/33] Fix positionCell not properly teleporting actors from inactive to active cells (Fixes #1516) --- apps/openmw/mwworld/worldimp.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 625942da7..4386f5b0d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -989,9 +989,18 @@ namespace MWWorld } else { - if (!mWorldScene->isCellActive(*currCell)) - ptr.getClass().copyToCell(ptr, *newCell, pos); - else if (!mWorldScene->isCellActive(*newCell)) + if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell)) + { + MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); + mWorldScene->addObjectToScene(newPtr); + + std::string script = newPtr.getClass().getScript(newPtr); + if (!script.empty()) { + mLocalScripts.add(script, newPtr); + } + addContainerScripts(newPtr, newCell); + } + else if (!mWorldScene->isCellActive(*newCell) && mWorldScene->isCellActive(*currCell)) { mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); From 28feb260eb8fd675dca04f8da2dc0dc88e3e043a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 19:34:53 +0200 Subject: [PATCH 03/33] Implement disposition/distance based aggression (Fixes #1520) --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 3 +-- apps/openmw/mwdialogue/filter.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 22 +++++++--------- .../mwmechanics/mechanicsmanagerimp.cpp | 25 +++++++++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ 7 files changed, 42 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 30a576a15..07d4eb2bc 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -191,6 +191,8 @@ namespace MWBase virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual void clear() = 0; + + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 10c318367..59c1087f9 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -345,7 +345,7 @@ namespace MWClass getCreatureStats(ptr).setAttacked(true); // Self defense - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80 + if (!attacker.isEmpty() && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker) && (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures // (they have no movement or attacks anyway) MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6d5ff9f7f..2b4c54e24 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -624,9 +624,8 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. // Attacking peaceful NPCs is a crime - // anything below 80 is considered peaceful (see Actors::updateActor) if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && - ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) + !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); getCreatureStats(ptr).setAttacked(true); diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index d301e88aa..08cdb1d00 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -530,7 +530,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_ShouldAttack: - return mActor.getClass().getCreatureStats(mActor).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() >= 80; + return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, + MWBase::Environment::get().getWorld()->getPlayerPtr()); case SelectWrapper::Function_CreatureTargetted: diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e9e9fbe28..ddb3087dd 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -201,32 +201,28 @@ namespace MWMechanics || (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target return; - float fight; + bool aggressive; if (againstPlayer) - fight = actor1.getClass().getCreatureStats(actor1).getAiSetting(CreatureStats::AI_Fight).getModified(); + aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); else { - fight = 0; + aggressive = false; // if one of actors is creature then we should make a decision to start combat or not // NOTE: function doesn't take into account combat between 2 creatures if (!actor1.getClass().isNpc()) { // if creature is hostile then it is necessarily to start combat - if (creatureStats.isHostile()) fight = 100; - else fight = creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified(); + if (creatureStats.isHostile()) aggressive = true; + else aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); } } - ESM::Position actor1Pos = actor1.getRefData().getPosition(); - ESM::Position actor2Pos = actor2.getRefData().getPosition(); - float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos)); - - if( (fight == 100 && d <= 5000) - || (fight >= 95 && d <= 3000) - || (fight >= 90 && d <= 2000) - || (fight >= 80 && d <= 1000)) + if(aggressive) { + const ESM::Position& actor1Pos = actor2.getRefData().getPosition(); + const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); + float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos)); if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d)) { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 79b121b31..4d6930135 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1098,4 +1098,29 @@ namespace MWMechanics { mActors.clear(); } + + bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) + { + Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); + Ogre::Vector3 pos2 (target.getRefData().getPosition().pos); + + float d = pos1.distance(pos2); + + static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( + "iFightDistanceBase")->getInt(); + static float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find( + "fFightDistanceMultiplier")->getFloat(); + static float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get().find( + "fFightDispMult")->getFloat(); + + int disposition = 50; + if (ptr.getClass().isNpc()) + disposition = getDerivedDisposition(ptr); + + int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + + (iFightDistanceBase - fFightDistanceMultiplier * d) + + ((50 - disposition) * fFightDispMult); + + return (fight >= 100); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 714537a38..677d60ae5 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -156,6 +156,8 @@ namespace MWMechanics virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual void clear(); + + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); }; } From 8ac019611dc3ae875e5a86a4e6e567bffb0d1b99 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 20:23:57 +0200 Subject: [PATCH 04/33] Fix Potion use action removing the potion even when the action is not executed (Fixes #1521) --- apps/openmw/mwclass/potion.cpp | 7 +------ apps/openmw/mwworld/actionapply.cpp | 20 +++++++++++++------- apps/openmw/mwworld/actionapply.hpp | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index d7cb0cd9b..440121d35 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -166,13 +166,8 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - // remove used potion (assume it is present in inventory) - ptr.getContainerStore()->remove(ptr, 1, actor); - boost::shared_ptr action ( - new MWWorld::ActionApply (actor, ref->mBase->mId)); + new MWWorld::ActionApply (ptr, ref->mBase->mId)); action->setSound ("Drink"); diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp index 6b12cc3e6..bfd64c85d 100644 --- a/apps/openmw/mwworld/actionapply.cpp +++ b/apps/openmw/mwworld/actionapply.cpp @@ -6,30 +6,36 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/containerstore.hpp" + namespace MWWorld { - ActionApply::ActionApply (const Ptr& target, const std::string& id) - : Action (false, target), mId (id) + ActionApply::ActionApply (const Ptr& object, const std::string& id) + : Action (false, object), mId (id) {} void ActionApply::executeImp (const Ptr& actor) { MWBase::Environment::get().getWorld()->breakInvisibility(actor); - getTarget().getClass().apply (getTarget(), mId, actor); + actor.getClass().apply (actor, mId, actor); + + actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor); } - ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id, + ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& object, const std::string& id, int skillIndex, int usageType) - : Action (false, target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) + : Action (false, object), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) {} void ActionApplyWithSkill::executeImp (const Ptr& actor) { MWBase::Environment::get().getWorld()->breakInvisibility(actor); - if (getTarget().getClass().apply (getTarget(), mId, actor) && mUsageType!=-1) - getTarget().getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType); + if (actor.getClass().apply (actor, mId, actor) && mUsageType!=-1) + actor.getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType); + + actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor); } } diff --git a/apps/openmw/mwworld/actionapply.hpp b/apps/openmw/mwworld/actionapply.hpp index 669a19067..5294745a6 100644 --- a/apps/openmw/mwworld/actionapply.hpp +++ b/apps/openmw/mwworld/actionapply.hpp @@ -16,7 +16,7 @@ namespace MWWorld public: - ActionApply (const Ptr& target, const std::string& id); + ActionApply (const Ptr& object, const std::string& id); }; class ActionApplyWithSkill : public Action @@ -29,7 +29,7 @@ namespace MWWorld public: - ActionApplyWithSkill (const Ptr& target, const std::string& id, + ActionApplyWithSkill (const Ptr& object, const std::string& id, int skillIndex, int usageType); }; } From ffb6f5d555fd850ecdd2a9ff0930f58c309f0161 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 01:10:37 +0200 Subject: [PATCH 05/33] Use fFight GMSTs to control attacks in response to crimes --- apps/openmw/mwbase/mechanicsmanager.hpp | 5 +- apps/openmw/mwmechanics/actors.cpp | 11 -- apps/openmw/mwmechanics/aisequence.cpp | 5 + .../mwmechanics/mechanicsmanagerimp.cpp | 137 ++++++++++++------ .../mwmechanics/mechanicsmanagerimp.hpp | 4 +- 5 files changed, 105 insertions(+), 57 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 07d4eb2bc..91d0ed9bf 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -120,6 +120,7 @@ namespace MWBase /** * @brief Commit a crime. If any actors witness the crime and report it, * reportCrime will be called automatically. + * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @return was the crime reported? */ @@ -192,7 +193,9 @@ namespace MWBase virtual void clear() = 0; - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; + /// @param bias Can be used to add an additional aggression bias towards the target, + /// making it more likely for the function to return true. + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ddb3087dd..5e46a11a3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -789,7 +789,6 @@ namespace MWMechanics if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile()) { - /// \todo Move me! I shouldn't be here... const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()); // Force dialogue on sight if bounty is greater than the cutoff @@ -817,7 +816,6 @@ namespace MWMechanics creatureStats.getAiSequence().stopCombat(); // Reset factors to attack - // TODO: Not a complete list, disposition changes? creatureStats.setHostile(false); creatureStats.setAttacked(false); creatureStats.setAlarmed(false); @@ -825,15 +823,6 @@ namespace MWMechanics // Update witness crime id npcStats.setCrimeId(-1); } - else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue) - { - if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPursue(player), ptr); - else - { - MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); - } - } } } } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 05723c575..3aeeee65a 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -221,6 +221,11 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor) { return; // target is already pursued } + if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat + && static_cast(*iter)->getTarget() == static_cast(&package)->getTarget()) + { + return; // already in combat with this actor + } else if ((*iter)->getTypeId() == AiPackage::TypeIdWander) static_cast(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos)); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4d6930135..cb59d188f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -14,6 +14,7 @@ #include "../mwworld/player.hpp" #include "../mwmechanics/aicombat.hpp" +#include "../mwmechanics/aipursue.hpp" #include @@ -836,7 +837,7 @@ namespace MWMechanics const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); // What amount of alarm did this crime generate? - int alarm; + int alarm = 0; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) alarm = esmStore.get().find("iAlarmTresspass")->getInt(); else if (type == OT_Pickpocket) @@ -847,17 +848,14 @@ namespace MWMechanics alarm = esmStore.get().find("iAlarmKilling")->getInt(); else if (type == OT_Theft) alarm = esmStore.get().find("iAlarmStealing")->getInt(); - else - return false; - // Innocent until proven guilty bool reported = false; // Find all the actors within the alarm radius std::vector neighbors; Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos); - int radius = esmStore.get().find("fAlarmRadius")->getInt(); + float radius = esmStore.get().find("fAlarmRadius")->getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -865,9 +863,9 @@ namespace MWMechanics if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) neighbors.push_back(victim); - int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); + bool victimAware = false; - // Find actors who witnessed the crime + // Find actors who directly witnessed the crime for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if (*it == player) continue; // not the player @@ -875,22 +873,13 @@ namespace MWMechanics // Was the crime seen? if (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) { - // TODO: Add more messages + if (*it == victim) + victimAware = true; + + // TODO: are there other messages? if (type == OT_Theft) MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); - if (*it == victim) - { - // Self-defense - // The victim is aware of the criminal/assailant. If being assaulted, fight back now - // (regardless of whether the assault is reported or not) - // This applies to both NPCs and creatures - - // ... except if this is a guard: then the player is given a chance to pay a fine / go to jail instead - if (type == OT_Assault && !it->getClass().isClass(*it, "guard")) - MWBase::Environment::get().getMechanicsManager()->startCombat(victim, player); - } - // Crime reporting only applies to NPCs if (!it->getClass().isNpc()) continue; @@ -899,32 +888,19 @@ namespace MWMechanics if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { reported = true; - - // Tell everyone, including yourself - for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) - { - if ( *it1 == player - || !it1->getClass().isNpc()) continue; // not the player and is an NPC - - // Will other witnesses paticipate in crime - if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm - || type == OT_Assault ) - { - it1->getClass().getNpcStats(*it1).setCrimeId(id); - } - - // Mark as Alarmed for dialogue - it1->getClass().getCreatureStats(*it1).setAlarmed(true); - } } } } + if (reported) reportCrime(player, victim, type, arg); + else if (victimAware && !victim.isEmpty() && type == OT_Assault) + startCombat(victim, player); + return reported; } - void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); @@ -944,7 +920,7 @@ namespace MWMechanics } MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); - ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() + arg); // If committing a crime against a faction member, expell from the faction @@ -953,9 +929,81 @@ namespace MWMechanics std::string factionID; if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; - if (ptr.getClass().getNpcStats(ptr).isSameFaction(victim.getClass().getNpcStats(victim))) + if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim))) { - ptr.getClass().getNpcStats(ptr).expell(factionID); + player.getClass().getNpcStats(player).expell(factionID); + } + } + + // Make surrounding actors within alarm distance respond to the crime + std::vector neighbors; + + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + + Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos); + float radius = esmStore.get().find("fAlarmRadius")->getFloat(); + + mActors.getObjectsInRange(from, radius, neighbors); + + // victim should be considered even beyond alarm radius + if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) + neighbors.push_back(victim); + + int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); + + // What amount of provocation did this crime generate? + // Controls whether witnesses will engage combat with the criminal. + int fight = 0; + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + fight = esmStore.get().find("iFightTrespass")->getInt(); + else if (type == OT_Pickpocket) + fight = esmStore.get().find("iFightPickpocket")->getInt(); + else if (type == OT_Assault) // Note: iFightAttack is for the victim, iFightAttacking for witnesses? + fight = esmStore.get().find("iFightAttack")->getInt(); + else if (type == OT_Murder) + fight = esmStore.get().find("iFightKilling")->getInt(); + else if (type == OT_Theft) + fight = esmStore.get().find("fFightStealing")->getFloat(); + + const int iFightAttacking = esmStore.get().find("iFightAttacking")->getInt(); + + // Tell everyone (including the original reporter) in alarm range + for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + { + if ( *it == player + || !it->getClass().isNpc()) continue; + + int aggression = fight; + + // Note: iFightAttack is used for the victim, iFightAttacking for witnesses? + if (*it != victim && type == OT_Assault) + aggression = iFightAttacking; + + if (it->getClass().isClass(*it, "guard")) + { + // Mark as Alarmed for dialogue + it->getClass().getCreatureStats(*it).setAlarmed(true); + + // Set the crime ID, which we will use to calm down participants + // once the bounty has been paid. + it->getClass().getNpcStats(*it).setCrimeId(id); + + it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it); + } + else + { + bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression); + if (aggressive) + { + startCombat(*it, player); + + // Set the crime ID, which we will use to calm down participants + // once the bounty has been paid. + it->getClass().getNpcStats(*it).setCrimeId(id); + + // Mark as Alarmed for dialogue + it->getClass().getCreatureStats(*it).setAlarmed(true); + } } } } @@ -1099,7 +1147,7 @@ namespace MWMechanics mActors.clear(); } - bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) + bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias) { Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); Ogre::Vector3 pos2 (target.getRefData().getPosition().pos); @@ -1117,9 +1165,10 @@ namespace MWMechanics if (ptr.getClass().isNpc()) disposition = getDerivedDisposition(ptr); - int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + (iFightDistanceBase - fFightDistanceMultiplier * d) - + ((50 - disposition) * fFightDispMult); + + ((50 - disposition) * fFightDispMult)) + + bias; return (fight >= 100); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 677d60ae5..0fee72b77 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -157,7 +157,9 @@ namespace MWMechanics virtual void clear(); - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); + /// @param bias Can be used to add an additional aggression bias towards the target, + /// making it more likely for the function to return true. + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0); }; } From ec66484472b235ed1cfcd310b312683daa3a0544 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 01:19:14 +0200 Subject: [PATCH 06/33] Fix forceGreeting with explicit references (Fixes #1518) --- components/compiler/extensions0.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c4b078ae8..5733a4170 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -167,7 +167,6 @@ namespace Compiler extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex); extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic); extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice); - extensions.registerInstruction("forcegreeting","",opcodeForceGreeting); extensions.registerInstruction("forcegreeting","",opcodeForceGreeting, opcodeForceGreetingExplicit); extensions.registerInstruction("goodbye", "", opcodeGoodbye); From 3801dfb4bab2ae5fdde3c24e65edeed006fdc2d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 01:53:09 +0200 Subject: [PATCH 07/33] Add delay to sneak icon update and skill progress (Fixes #1321) --- apps/openmw/mwmechanics/actors.cpp | 58 ++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5e46a11a3..dcbb6b8bd 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1074,6 +1074,9 @@ namespace MWMechanics isBattleMusic = true; } + static float sneakTimer = 0.f; // times update of sneak icon + static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice" + // if player is in sneak state see if anyone detects him if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak)) { @@ -1081,27 +1084,54 @@ namespace MWMechanics const int radius = esmStore.get().find("fSneakUseDist")->getInt(); bool detected = false; - for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->getFloat(); + + if (sneakTimer >= fSneakUseDelay) + sneakTimer = 0.f; + + if (sneakTimer == 0.f) { - if (iter->first == player) // not the player - continue; + // Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress. + bool avoidedNotice = false; - // is the player in range and can they be detected - if ( (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius) - && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first) - && MWBase::Environment::get().getWorld()->getLOS(player, iter->first)) + for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - detected = true; - MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); - break; - } - } + if (iter->first == player) // not the player + continue; - if (!detected) - MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); + // is the player in range and can they be detected + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius + && MWBase::Environment::get().getWorld()->getLOS(player, iter->first)) + { + if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first)) + { + detected = true; + avoidedNotice = false; + MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + break; + } + else if (!detected) + avoidedNotice = true; + } + } + + if (sneakSkillTimer >= fSneakUseDelay) + sneakSkillTimer = 0.f; + + if (avoidedNotice && sneakSkillTimer == 0.f) + player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0); + + if (!detected) + MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); + } + sneakTimer += duration; + sneakSkillTimer += duration; } else + { + sneakTimer = 0.f; MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + } } } void Actors::restoreDynamicStats(bool sleep) From 2477456f993acdc1f26215da51603d36b7acdfcd Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 03:54:41 +0200 Subject: [PATCH 08/33] Implement Murder crimes and OnMurder instruction (Fixes #1315) --- apps/openmw/mwclass/npc.cpp | 13 ++++++ apps/openmw/mwmechanics/activespells.hpp | 8 ++-- apps/openmw/mwmechanics/actors.cpp | 40 +++++++++++++++++++ apps/openmw/mwmechanics/creaturestats.cpp | 19 ++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 7 ++++ .../mwmechanics/mechanicsmanagerimp.cpp | 15 +++++-- apps/openmw/mwscript/docs/vmformat.txt | 4 +- apps/openmw/mwscript/statsextensions.cpp | 21 ++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 + components/esm/creaturestats.cpp | 6 +++ components/esm/creaturestats.hpp | 1 + 12 files changed, 128 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 2b4c54e24..1a33747f2 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -628,6 +628,8 @@ namespace MWClass !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + bool wasDead = getCreatureStats(ptr).isDead(); + getCreatureStats(ptr).setAttacked(true); if(!successful) @@ -751,6 +753,17 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } + + if (!wasDead && getCreatureStats(ptr).isDead()) + { + // NPC was killed + // Simple check for who attacked first: if the player attacked first, a crimeId should be set + // Doesn't handle possible edge case where no one reported the assault, but in such a case, + // for bystanders it is not possible to tell who attacked first, anyway. + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); + } } void Npc::block(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 6c4c93128..9c1a5e613 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -40,6 +40,10 @@ namespace MWMechanics void readState (const ESM::ActiveSpells& state); void writeState (ESM::ActiveSpells& state) const; + TIterator begin() const; + + TIterator end() const; + private: mutable TContainer mSpells; @@ -57,10 +61,6 @@ namespace MWMechanics const TContainer& getActiveSpells() const; - TIterator begin() const; - - TIterator end() const; - public: ActiveSpells(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index dcbb6b8bd..0c5a2abec 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -342,6 +342,8 @@ namespace MWMechanics CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr); const MagicEffects &effects = creatureStats.getMagicEffects(); + bool wasDead = creatureStats.isDead(); + // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { @@ -454,6 +456,44 @@ namespace MWMechanics } creatureStats.setHealth(health); + if (!wasDead && creatureStats.isDead()) + { + // The actor was killed by a magic effect. Figure out if the player was responsible for it. + const ActiveSpells& spells = creatureStats.getActiveSpells(); + for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + const ActiveSpells::ActiveSpellParams& spell = it->second; + for (std::vector::const_iterator effectIt = spell.mEffects.begin(); + effectIt != spell.mEffects.end(); ++effectIt) + { + int effectId = effectIt->mEffectId; + bool isDamageEffect = false; + for (unsigned int i=0; igetPlayerPtr(); + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId); + if (isDamageEffect && caster == player) + { + // Simple check for who attacked first: if the player attacked first, a crimeId should be set + // Doesn't handle possible edge case where no one reported the assault, but in such a case, + // for bystanders it is not possible to tell who attacked first, anyway. + if (ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 + && ptr != player) + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); + break; + } + } + } + } + // TODO: dirty flag for magic effects to avoid some unnecessary work below? // Update bound effects diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index e26e7a8ba..af35a109a 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -14,7 +14,7 @@ namespace MWMechanics int CreatureStats::sActorId = 0; CreatureStats::CreatureStats() - : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), + : mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), mAttackingOrSpell(false), @@ -245,6 +245,21 @@ namespace MWMechanics mDied = false; } + bool CreatureStats::hasBeenMurdered() const + { + return mMurdered; + } + + void CreatureStats::notifyMurder() + { + mMurdered = true; + } + + void CreatureStats::clearHasBeenMurdered() + { + mMurdered = false; + } + void CreatureStats::resurrect() { if (mDead) @@ -479,6 +494,7 @@ namespace MWMechanics state.mDead = mDead; state.mDied = mDied; + state.mMurdered = mMurdered; state.mFriendlyHits = mFriendlyHits; state.mTalkedTo = mTalkedTo; state.mAlarmed = mAlarmed; @@ -527,6 +543,7 @@ namespace MWMechanics mDead = state.mDead; mDied = state.mDied; + mMurdered = state.mMurdered; mFriendlyHits = state.mFriendlyHits; mTalkedTo = state.mTalkedTo; mAlarmed = state.mAlarmed; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index d02af8fb6..b365a0b89 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -35,6 +35,7 @@ namespace MWMechanics AiSequence mAiSequence; bool mDead; bool mDied; + bool mMurdered; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; @@ -170,6 +171,12 @@ namespace MWMechanics void clearHasDied(); + bool hasBeenMurdered() const; + + void clearHasBeenMurdered(); + + void notifyMurder(); + void resurrect(); bool hasCommonDisease() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index cb59d188f..612a4ea84 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -868,10 +868,16 @@ namespace MWMechanics // Find actors who directly witnessed the crime for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { - if (*it == player) continue; // not the player + if (*it == player) + continue; // skip player + if (it->getClass().getCreatureStats(*it).isDead()) + continue; // Was the crime seen? - if (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) + if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) + // Murder crime can be reported even if no one saw it (hearing is enough, I guess). + // TODO: Add mod support for stealth executions! + || (type == OT_Murder && *it != victim)) { if (*it == victim) victimAware = true; @@ -904,6 +910,9 @@ namespace MWMechanics { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); + if (type == OT_Murder && !victim.isEmpty()) + victim.getClass().getCreatureStats(victim).notifyMurder(); + // Bounty for each type of crime if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) arg = store.find("iCrimeTresspass")->getInt(); @@ -971,7 +980,7 @@ namespace MWMechanics for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if ( *it == player - || !it->getClass().isNpc()) continue; + || !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue; int aggression = fight; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index ab69bc4b4..c125985c0 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -398,5 +398,7 @@ op 0x2000245: ClearInfoActor op 0x2000246: ClearInfoActor, explicit op 0x2000247: BetaComment op 0x2000248: BetaComment, explicit +op 0x2000249: OnMurder +op 0x200024a: OnMurder, explicit -opcodes 0x2000249-0x3ffffff unused +opcodes 0x200024b-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 8b61237a1..a3bc223aa 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1072,6 +1072,25 @@ namespace MWScript } }; + template + class OpOnMurder : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = + ptr.getClass().getCreatureStats (ptr).hasBeenMurdered(); + + if (value) + ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered(); + + runtime.push (value); + } + }; + template class OpOnKnockout : public Interpreter::Opcode0 { @@ -1264,6 +1283,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath); interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath); + interpreter.installSegment5 (Compiler::Stats::opcodeOnMurder, new OpOnMurder); + interpreter.installSegment5 (Compiler::Stats::opcodeOnMurderExplicit, new OpOnMurder); interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout); interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 5733a4170..2d140044c 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -449,6 +449,7 @@ namespace Compiler extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit); + extensions.registerFunction ("onmurder", 'l', "", opcodeOnMurder, opcodeOnMurderExplicit); extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit); extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 8716cdd18..ed6f1df92 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -385,6 +385,8 @@ namespace Compiler const int opcodeLowerRankExplicit = 0x20001eb; const int opcodeOnDeath = 0x20001fc; const int opcodeOnDeathExplicit = 0x2000205; + const int opcodeOnMurder = 0x2000249; + const int opcodeOnMurderExplicit = 0x200024a; const int opcodeOnKnockout = 0x2000240; const int opcodeOnKnockoutExplicit = 0x2000241; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 84902d8c2..0a9a36109 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -21,6 +21,9 @@ void ESM::CreatureStats::load (ESMReader &esm) mDied = false; esm.getHNOT (mDied, "DIED"); + mMurdered = false; + esm.getHNOT (mMurdered, "MURD"); + mFriendlyHits = 0; esm.getHNOT (mFriendlyHits, "FRHT"); @@ -127,6 +130,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mDied) esm.writeHNT ("DIED", mDied); + if (mMurdered) + esm.writeHNT ("MURD", mMurdered); + if (mFriendlyHits) esm.writeHNT ("FRHT", mFriendlyHits); diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 628bfee0a..7ae57da16 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -38,6 +38,7 @@ namespace ESM bool mDead; bool mDied; + bool mMurdered; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; From cb6f1d3a52b50a858487bcfa571b5bb9aa1721af Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 14:46:18 +0200 Subject: [PATCH 09/33] Fix wait dialog arrow button step (Fixes #1524) --- files/mygui/openmw_wait_dialog.layout | 2 ++ 1 file changed, 2 insertions(+) diff --git a/files/mygui/openmw_wait_dialog.layout b/files/mygui/openmw_wait_dialog.layout index eeb7012eb..7c065038d 100644 --- a/files/mygui/openmw_wait_dialog.layout +++ b/files/mygui/openmw_wait_dialog.layout @@ -19,6 +19,8 @@ + + From 4f9ebd148c06770256d32e37aac02a633f7db9bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 15:25:54 +0200 Subject: [PATCH 10/33] Fix broken AI movement on Z axis --- apps/openmw/mwmechanics/aicombat.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 846f6d8dc..f5881d605 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -470,6 +470,8 @@ namespace MWMechanics if(preferShortcut) { + if (canMoveByZ) + mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget); mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget); mForceNoShortcut = false; mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0; From 1244da85df5ab746e4eafa8d34ddc867c1a7f173 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 16:27:33 +0200 Subject: [PATCH 11/33] Make Detect Life spell detect NPCs when in werewolf form (Fixes #1527) --- apps/openmw/mwgui/hud.cpp | 2 ++ apps/openmw/mwgui/mapwindow.cpp | 15 +++++++++++++++ apps/openmw/mwgui/mapwindow.hpp | 6 +++++- apps/openmw/mwworld/worldimp.cpp | 24 +++++++++++++++++------- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index d87ac7ec5..b39fd0db7 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -384,6 +384,8 @@ namespace MWGui void HUD::onFrame(float dt) { + LocalMapBase::onFrame(dt); + mCellNameTimer -= dt; mWeaponSpellTimer -= dt; if (mCellNameTimer < 0) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 29c065f3d..db3cfadde 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -36,6 +36,7 @@ namespace MWGui , mLastDirectionX(0.0f) , mLastDirectionY(0.0f) , mCompass(NULL) + , mMarkerUpdateTimer(0.0f) { } @@ -345,6 +346,18 @@ namespace MWGui markerWidget->setUserString("IsMarker", "true"); markerWidget->setUserData(markerPos); markerWidget->setColour(markerColour); + mMarkerWidgets.push_back(markerWidget); + } + } + + void LocalMapBase::onFrame(float dt) + { + mMarkerUpdateTimer += dt; + + if (mMarkerUpdateTimer >= 0.25) + { + mMarkerUpdateTimer = 0; + updateMarkers(); } } @@ -471,6 +484,8 @@ namespace MWGui void MapWindow::onFrame(float dt) { + LocalMapBase::onFrame(dt); + for (std::vector::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it) { mGlobalMapRender->exploreCell(it->first, it->second); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index d23b0c228..c73e5d7a1 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -35,6 +35,8 @@ namespace MWGui void setPlayerDir(const float x, const float y); void setPlayerPos(const float x, const float y); + void onFrame(float dt); + bool toggleFogOfWar(); struct MarkerPosition @@ -73,12 +75,14 @@ namespace MWGui virtual void notifyMapChanged() {} // Update markers (Detect X effects, Mark/Recall effects) - // Note, door markers handled in setActiveCell + // Note, door markers are handled in setActiveCell void updateMarkers(); void addDetectionMarkers(int type); OEngine::GUI::Layout* mLayout; + float mMarkerUpdateTimer; + bool mMapDragAndDrop; float mLastPositionX; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4386f5b0d..f6573096d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2445,14 +2445,15 @@ namespace MWWorld if (!ptr.getRefData().isEnabled()) return true; - // Consider references inside containers as well - if (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name()) + // Consider references inside containers as well (except if we are looking for a Creature, they cannot be in containers) + if (mType != World::Detect_Creature && + (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name())) { MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); { for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - if (needToAdd(*it)) + if (needToAdd(*it, mDetector)) { mOut.push_back(ptr); return true; @@ -2461,16 +2462,25 @@ namespace MWWorld } } - if (needToAdd(ptr)) + if (needToAdd(ptr, mDetector)) mOut.push_back(ptr); return true; } - bool needToAdd (MWWorld::Ptr ptr) + bool needToAdd (MWWorld::Ptr ptr, MWWorld::Ptr detector) { - if (mType == World::Detect_Creature && ptr.getClass().getTypeName() != typeid(ESM::Creature).name()) - return false; + if (mType == World::Detect_Creature) + { + // If in werewolf form, this detects only NPCs, otherwise only creatures + if (detector.getClass().isNpc() && detector.getClass().getNpcStats(detector).isWerewolf()) + { + if (ptr.getClass().getTypeName() != typeid(ESM::NPC).name()) + return false; + } + else if (ptr.getClass().getTypeName() != typeid(ESM::Creature).name()) + return false; + } if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr)) return false; if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty()) From 1dc9e151cb63355844a9347e66b02729db64f787 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 16:51:59 +0200 Subject: [PATCH 12/33] Count werewolf kills (Fixes #1525) --- apps/openmw/mwclass/npc.cpp | 7 ++++++- apps/openmw/mwmechanics/actors.cpp | 12 +++++++++--- apps/openmw/mwmechanics/npcstats.cpp | 7 +++++++ apps/openmw/mwmechanics/npcstats.hpp | 3 +++ components/compiler/extensions0.cpp | 2 +- 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 1a33747f2..a93cae9ba 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -757,11 +757,16 @@ namespace MWClass if (!wasDead && getCreatureStats(ptr).isDead()) { // NPC was killed + if (!attacker.isEmpty() && attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()) + { + attacker.getClass().getNpcStats(attacker).addWerewolfKill(); + } + // Simple check for who attacked first: if the player attacked first, a crimeId should be set // Doesn't handle possible edge case where no one reported the assault, but in such a case, // for bystanders it is not possible to tell who attacked first, anyway. MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - if (ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) + if (attacker == player && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0c5a2abec..d72ab7db8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -460,6 +460,9 @@ namespace MWMechanics { // The actor was killed by a magic effect. Figure out if the player was responsible for it. const ActiveSpells& spells = creatureStats.getActiveSpells(); + bool killedByPlayer = false; + bool murderedByPlayer = false; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ActiveSpells::ActiveSpellParams& spell = it->second; @@ -478,20 +481,23 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::AbsorbHealth) isDamageEffect = true; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId); if (isDamageEffect && caster == player) { + killedByPlayer = true; // Simple check for who attacked first: if the player attacked first, a crimeId should be set // Doesn't handle possible edge case where no one reported the assault, but in such a case, // for bystanders it is not possible to tell who attacked first, anyway. if (ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) - MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); - break; + murderedByPlayer = true; } } } + if (murderedByPlayer) + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); + if (killedByPlayer && player.getClass().getNpcStats(player).isWerewolf()) + player.getClass().getNpcStats(player).addWerewolfKill(); } // TODO: dirty flag for magic effects to avoid some unnecessary work below? diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 642e1cfe1..579969f9d 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -396,6 +396,8 @@ void MWMechanics::NpcStats::setWerewolf (bool set) { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + mWerewolfKills = 0; + for(size_t i = 0;i < ESM::Attribute::Length;i++) { mWerewolfAttributes[i] = getAttribute(i); @@ -427,6 +429,11 @@ int MWMechanics::NpcStats::getWerewolfKills() const return mWerewolfKills; } +void MWMechanics::NpcStats::addWerewolfKill() +{ + ++mWerewolfKills; +} + int MWMechanics::NpcStats::getProfit() const { return mProfit; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 185a58b94..a066760d0 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -126,6 +126,9 @@ namespace MWMechanics int getWerewolfKills() const; + /// Increments mWerewolfKills by 1. + void addWerewolfKill(); + float getTimeToStartDrowning() const; /// Sets time left for the creature to drown if it stays underwater. /// @param time value from [0,20] diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 2d140044c..efd45f912 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -441,7 +441,7 @@ namespace Compiler extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace, opcodeGetRaceExplicit); - extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills); + extensions.registerFunction ("getwerewolfkills", 'l', "", opcodeGetWerewolfKills); extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit); extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit); extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit); From a3ea7cb9563ccaf93372893e7c4b05299202a917 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 17:04:41 +0200 Subject: [PATCH 13/33] Ignore distance when considering aggression due to crime (seems to work better, all balmora mages guild members now come to help when one is attacked) --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 8 +++++--- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 91d0ed9bf..f2b71bd4c 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -195,7 +195,7 @@ namespace MWBase /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0) = 0; + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0; }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 612a4ea84..f4ffa499c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1001,7 +1001,7 @@ namespace MWMechanics } else { - bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression); + bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true); if (aggressive) { startCombat(*it, player); @@ -1156,12 +1156,14 @@ namespace MWMechanics mActors.clear(); } - bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias) + bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias, bool ignoreDistance) { Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); Ogre::Vector3 pos2 (target.getRefData().getPosition().pos); - float d = pos1.distance(pos2); + float d = 0; + if (!ignoreDistance) + d = pos1.distance(pos2); static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( "iFightDistanceBase")->getInt(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 0fee72b77..596e6887e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -159,7 +159,7 @@ namespace MWMechanics /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0); + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false); }; } From 666dbc6ddca39329240cef6141bccb943e1b773e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 17:18:30 +0200 Subject: [PATCH 14/33] Disable QuickKeysMenu in werewolf form --- apps/openmw/mwinput/inputmanagerimp.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 15b5abde6..cd3be4b42 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -26,7 +26,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "../mwdialogue/dialoguemanagerimp.hpp" @@ -826,6 +826,14 @@ namespace MWInput void InputManager::quickKey (int index) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (player.getClass().getNpcStats(player).isWerewolf()) + { + // Cannot use items or spells while in werewolf form + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + return; + } + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) MWBase::Environment::get().getWindowManager()->activateQuickKey (index); } @@ -834,7 +842,18 @@ namespace MWInput { if (!MWBase::Environment::get().getWindowManager()->isGuiMode () && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (player.getClass().getNpcStats(player).isWerewolf()) + { + // Cannot use items or spells while in werewolf form + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + return; + } + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); + + } else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) { while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows MWBase::Environment::get().getWindowManager()->getCurrentModal()->exit(); From d4678a8d5517a8d897c64151eba63f2a02c2bd45 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 18:33:53 +0200 Subject: [PATCH 15/33] Fix level up dialogue layout (Fixes #1393) --- apps/openmw/mwgui/levelupdialog.cpp | 5 +++-- apps/openmw/mwgui/tooltips.cpp | 8 ++------ apps/openmw/mwgui/widgets.cpp | 7 +++++-- files/mygui/openmw_levelup_dialog.layout | 24 ++++++++++++++++-------- files/mygui/openmw_text.skin.xml | 7 +++++++ 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 38995ac32..eaa822722 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -104,9 +104,10 @@ namespace MWGui int attribute = mSpentAttributes[i]; - int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 30; + int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 20; - MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(24+xdiff,-4); + MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(22+xdiff,0); + pos.top += (mAttributes[attribute]->getHeight() - image->getHeight())/2; image->setPosition(pos); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 16b010908..676a9ee63 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -233,13 +233,9 @@ namespace MWGui for (std::map::iterator it = userStrings.begin(); it != userStrings.end(); ++it) { - if (it->first == "ToolTipType" - || it->first == "ToolTipLayout" - || it->first == "IsMarker") - continue; - - size_t underscorePos = it->first.find("_"); + if (underscorePos == std::string::npos) + continue; std::string propertyKey = it->first.substr(0, underscorePos); std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index b30cf2bae..b83ca0b09 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -630,8 +630,11 @@ namespace MWGui MyGUI::IntSize AutoSizedButton::getRequestedSize() { - MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); - size.height = std::max(24, size.height); + MyGUI::IntSize padding(24, 8); + if (isUserString("TextPadding")) + padding = MyGUI::IntSize::parse(getUserString("TextPadding")); + + MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(padding.width,padding.height); return size; } diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index 765bf88a8..0b12d7a05 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -31,17 +31,18 @@ - - - - - - - - + + + + + + + + + @@ -55,6 +56,7 @@ + @@ -68,6 +70,7 @@ + @@ -81,6 +84,7 @@ + @@ -95,6 +99,7 @@ + @@ -108,6 +113,7 @@ + @@ -121,6 +127,7 @@ + @@ -134,6 +141,7 @@ + diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index d4c72c75b..4c1117cf5 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -32,6 +32,13 @@ + + + + + + + From 80f66e21576d96cb1f4b486c816451e826cf1827 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 21:16:32 +0200 Subject: [PATCH 16/33] Fix crash when avformat_open_input fails (Fixes #1522) --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 75f7ccae4..10a782b96 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -159,17 +159,21 @@ void FFmpeg_Decoder::open(const std::string &fname) mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) { - if (mFormatCtx->pb != NULL) + // "Note that a user-supplied AVFormatContext will be freed on failure". + if (mFormatCtx) { - if (mFormatCtx->pb->buffer != NULL) - { - av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; - } - av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + if (mFormatCtx->pb != NULL) + { + if (mFormatCtx->pb->buffer != NULL) + { + av_free(mFormatCtx->pb->buffer); + mFormatCtx->pb->buffer = NULL; + } + av_free(mFormatCtx->pb); + mFormatCtx->pb = NULL; + } + avformat_free_context(mFormatCtx); } - avformat_free_context(mFormatCtx); mFormatCtx = NULL; fail("Failed to allocate input stream"); } From 56bc5a9d39421c71b15a16883f0253e74ba99dde Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 21:27:41 +0200 Subject: [PATCH 17/33] Fix being able to steal undetected just after invisibility breaks --- apps/openmw/mwworld/worldimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f6573096d..645591cb8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2344,6 +2344,9 @@ namespace MWWorld actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); if (actor.getClass().hasInventoryStore(actor)) actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); + + // Normally updated once per frame, but here it is kinda important to do it right away. + MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor); } bool World::isDark() const From fe1e6a27199794da5c093ce4ff7e0f792e1987d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 22:24:56 +0200 Subject: [PATCH 18/33] Make Weakness effects apply to all subsequent effects within the same spell (Fixes #1150) --- apps/openmw/mwmechanics/spellcasting.cpp | 27 ++++++++++++++++++------ apps/openmw/mwmechanics/spellcasting.hpp | 9 ++++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index c996e90d6..26480ed03 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -148,13 +148,17 @@ namespace MWMechanics return school; } - float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell, const MagicEffects* effects) { const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( effectId); const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects(); + if (effects) + magicEffects = effects; float resisted = 0; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) @@ -165,9 +169,9 @@ namespace MWMechanics float resistance = 0; if (resistanceEffect != -1) - resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude; + resistance += magicEffects->get(resistanceEffect).mMagnitude; if (weaknessEffect != -1) - resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude; + resistance -= magicEffects->get(weaknessEffect).mMagnitude; float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); @@ -204,9 +208,10 @@ namespace MWMechanics return resisted; } - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell, const MagicEffects* effects) { - float resistance = getEffectResistance(effectId, actor, caster, spell); + float resistance = getEffectResistance(effectId, actor, caster, spell, effects); if (resistance >= 0) return 1 - resistance / 100.f; else @@ -261,6 +266,13 @@ namespace MWMechanics bool firstAppliedEffect = true; bool anyHarmfulEffect = false; + // HACK: cache target's magic effects here, and add any applied effects to it. Use the cached effects for determining resistance. + // This is required for Weakness effects in a spell to apply to any subsequent effects in the spell. + // Otherwise, they'd only apply after the whole spell was added. + MagicEffects targetEffects; + if (target.getClass().isActor()) + targetEffects += target.getClass().getCreatureStats(target).getMagicEffects(); + bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player"); for (std::vector::const_iterator effectIt (effects.mList.begin()); @@ -346,8 +358,7 @@ namespace MWMechanics // Try resisting if (magnitudeMult > 0 && target.getClass().isActor()) { - - magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell); + magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects); if (magnitudeMult == 0) { // Fully resisted, show message @@ -375,6 +386,8 @@ namespace MWMechanics effect.mDuration = effectIt->mDuration; effect.mMagnitude = magnitude; + targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); + appliedLastingEffects.push_back(effect); // For absorb effects, also apply the effect to the caster - but with a negative diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index dce4b792e..821d7ae0e 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -18,6 +18,7 @@ namespace ESM namespace MWMechanics { class EffectKey; + class MagicEffects; ESM::Skill::SkillEnum spellSchoolToSkill(int school); @@ -35,9 +36,13 @@ namespace MWMechanics int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); /// @return >=100 for fully resisted. can also return negative value for damage amplification. - float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); + /// @param effects Override the actor's current magicEffects. Useful if there are effects currently + /// being applied (but not applied yet) that should also be considered. + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); class CastSpell { From e95483c40f7c04168a09e64d45040146310ecc81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 01:41:07 +0200 Subject: [PATCH 19/33] Fix crash for on target spells cast by non-actors (Fixes #1529) --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 22 ++++++++++++++---- apps/openmw/mwmechanics/spellcasting.hpp | 9 ++++++-- apps/openmw/mwworld/projectilemanager.cpp | 28 ++++++++++++++++------- apps/openmw/mwworld/projectilemanager.hpp | 11 +++++++-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- 7 files changed, 57 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index df75ce64d..29f821e85 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -470,7 +470,7 @@ namespace MWBase virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName) = 0; + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) = 0; virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 26480ed03..6467730dd 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -340,7 +340,7 @@ namespace MWMechanics } // Try reflecting - if (!reflected && magnitudeMult > 0 && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) + if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) { int reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude; int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] @@ -465,14 +465,14 @@ namespace MWMechanics if (!appliedLastingEffects.empty()) { int casterActorId = -1; - if (caster.getClass().isActor()) + if (!caster.isEmpty() && caster.getClass().isActor()) casterActorId = caster.getClass().getCreatureStats(caster).getActorId(); target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, casterActorId); } // Notify the target actor they've been hit - if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor()) + if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor()) target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); } @@ -657,7 +657,9 @@ namespace MWMechanics getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed); if (!projectileModel.empty()) MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, - false, enchantment->mEffects, mCaster, mSourceName); + false, enchantment->mEffects, mCaster, mSourceName, + // Not needed, enchantments can only be cast by actors + Ogre::Vector3(1,0,0)); return true; } @@ -742,8 +744,18 @@ namespace MWMechanics float speed = 0; getProjectileInfo(spell->mEffects, projectileModel, sound, speed); if (!projectileModel.empty()) + { + Ogre::Vector3 fallbackDirection (0,1,0); + // Fall back to a "caster to target" direction if we have no other means of determining it + // (e.g. when cast by a non-actor) + if (!mTarget.isEmpty()) + fallbackDirection = + Ogre::Vector3(mTarget.getRefData().getPosition().pos)- + Ogre::Vector3(mCaster.getRefData().getPosition().pos); + MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, - false, spell->mEffects, mCaster, mSourceName); + false, spell->mEffects, mCaster, mSourceName, fallbackDirection); + } return true; } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 821d7ae0e..b526a4353 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -47,8 +47,8 @@ namespace MWMechanics class CastSpell { private: - MWWorld::Ptr mCaster; - MWWorld::Ptr mTarget; + MWWorld::Ptr mCaster; // May be empty + MWWorld::Ptr mTarget; // May be empty public: bool mStack; std::string mId; // ID of spell, potion, item etc @@ -59,8 +59,13 @@ namespace MWMechanics CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); bool cast (const ESM::Spell* spell); + + /// @note mCaster must be an actor bool cast (const MWWorld::Ptr& item); + + /// @note mCaster must be an NPC bool cast (const ESM::Ingredient* ingredient); + bool cast (const ESM::Potion* potion); /// @note Auto detects if spell, ingredient or potion diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 4e4f0b271..cfb407b4a 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -57,22 +57,32 @@ namespace MWWorld void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound, const std::string &spellId, float speed, bool stack, - const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName) + const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName, + const Ogre::Vector3& fallbackDirection) { - // Spawn at 0.75 * ActorHeight - float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75; + float height = 0; + if (OEngine::Physic::PhysicActor* actor = mPhysEngine.getCharacter(caster.getRefData().getHandle())) + height = actor->getHalfExtents().z * 2 * 0.75; // Spawn at 0.75 * ActorHeight - Ogre::Vector3 pos(actor.getRefData().getPosition().pos); + Ogre::Vector3 pos(caster.getRefData().getPosition().pos); pos.z += height; - Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + Ogre::Quaternion orient; + if (caster.getClass().isActor()) + orient = Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + else + orient = Ogre::Vector3::UNIT_Y.getRotationTo(fallbackDirection); MagicBoltState state; state.mSourceName = sourceName; state.mId = model; state.mSpellId = spellId; - state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); + state.mCaster = caster; + if (caster.getClass().isActor()) + state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); + else + state.mActorId = -1; state.mSpeed = speed; state.mStack = stack; state.mSoundId = sound; @@ -152,7 +162,9 @@ namespace MWWorld { MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); - MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + MWWorld::Ptr caster = it->mCaster; + if (caster.isEmpty()) + caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); if (!obstacle.isEmpty() && obstacle == caster) continue; diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index da965a4cf..4627d3b8a 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -39,9 +39,10 @@ namespace MWWorld ProjectileManager (Ogre::SceneManager* sceneMgr, OEngine::Physic::PhysicEngine& engine); + /// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used. void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName); + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection); void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); @@ -64,9 +65,15 @@ namespace MWWorld NifOgre::ObjectScenePtr mObject; Ogre::SceneNode* mNode; - // Actor who shot this projectile int mActorId; + // actorId doesn't work for non-actors, so we also keep track of the Ptr. + // For non-actors, the caster ptr is mainly needed to prevent the projectile + // from colliding with its caster. + // TODO: this will break when the game is saved and reloaded, since there is currently + // no way to write identifiers for non-actors to a savegame. + MWWorld::Ptr mCaster; + // MW-id of this projectile std::string mId; }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 645591cb8..0b623f854 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2329,9 +2329,9 @@ namespace MWWorld void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName) + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) { - mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName); + mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, caster, sourceName, fallbackDirection); } const std::vector& World::getContentFiles() const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ac1244652..db599b0de 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -546,7 +546,7 @@ namespace MWWorld virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName); + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection); virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); From 224163e5a2f4f1a8f073d5331df1d124cb4afb71 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 05:04:49 +0200 Subject: [PATCH 20/33] Fix console text becoming unreadable when selected (Fixes #1530) --- files/mygui/openmw_console.layout | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index 0c9a97d04..c88c1955d 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -13,10 +13,13 @@ + - + + + From 9a6737073fe400c0215daa7a93aaac9b45ac37af Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 15:33:09 +0200 Subject: [PATCH 21/33] Fix broken swimdeath in first person --- apps/openmw/mwmechanics/character.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6f0a255a9..ddfba51ce 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -409,13 +409,6 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I void CharacterController::playDeath(float startpoint, CharacterState death) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()) - { - // The first-person animations do not include death, so we need to - // force-switch to third person before playing the death animation. - MWBase::Environment::get().getWorld()->useDeathCamera(); - } - switch (death) { case CharState_SwimDeath: @@ -459,6 +452,13 @@ void CharacterController::playDeath(float startpoint, CharacterState death) void CharacterController::playRandomDeath(float startpoint) { + if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + // The first-person animations do not include death, so we need to + // force-switch to third person before playing the death animation. + MWBase::Environment::get().getWorld()->useDeathCamera(); + } + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) { mDeathState = CharState_SwimDeath; From 8a4227ec380f7e895ba449537167b9a5328c786f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 16:01:20 +0200 Subject: [PATCH 22/33] Heal player while in jail --- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0b623f854..6e975d25a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2615,6 +2615,8 @@ namespace MWWorld int days = std::max(1, bounty / iDaysinPrisonMod); advanceTime(days * 24); + for (int i=0; irest (true); std::set skills; for (int day=0; day buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); From 5645c9185beafb5dc81b0948b62b4f56d908e009 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 17:18:48 +0200 Subject: [PATCH 23/33] Fix location of local data path --- components/files/configurationmanager.cpp | 4 ++-- files/openmw.cfg | 2 +- files/openmw.cfg.local | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index ffa911b44..58d75d1fd 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -18,7 +18,7 @@ static const char* const openmwCfgFile = "openmw.cfg"; const char* const mwToken = "?mw?"; const char* const localToken = "?local?"; -const char* const userToken = "?user?"; +const char* const userDataToken = "?userdata?"; const char* const globalToken = "?global?"; ConfigurationManager::ConfigurationManager() @@ -40,7 +40,7 @@ void ConfigurationManager::setupTokensMapping() { mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); - mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath)); + mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } diff --git a/files/openmw.cfg b/files/openmw.cfg index 4633141a6..b67b79a96 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,4 +1,4 @@ data="?global?data" data="?mw?Data Files" -data-local="?user?data" +data-local="?userdata?data" resources=${OPENMW_RESOURCE_FILES} diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index d6ca2d554..6a578542d 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -1,5 +1,5 @@ data="?global?data" data="?mw?Data Files" data=./data -data-local="?user?data" +data-local="?userdata?data" resources=./resources From 4e71db70819a49ffcb8144dc16d27e1ed252b4bf Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 21:39:17 +0200 Subject: [PATCH 24/33] Savegame: Don't load/save deleted container items. This is currently pointless, and also causes new garbage being added on each load/save cycle: Container stores are first filled from ESM records, then cleared and filled from the savegame. The items from ESM records remain as deleted refs. --- apps/openmw/mwworld/containerstore.cpp | 2 ++ components/esm/inventorystate.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8a076c3fc..e330ddaee 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -92,6 +92,8 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { + if (iter->mData.getCount() == 0) + continue; ESM::ObjectState state; storeState (*iter, state); int slot = equipable ? getSlot (*iter) : -1; diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index 1b0bc772e..2154faa83 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -37,6 +37,8 @@ void ESM::InventoryState::load (ESMReader &esm) LightState state; int slot; read (esm, state, slot); + if (state.mCount == 0) + continue; mLights.push_back (std::make_pair (state, slot)); } else @@ -44,6 +46,8 @@ void ESM::InventoryState::load (ESMReader &esm) ObjectState state; int slot; read (esm, state, slot); + if (state.mCount == 0) + continue; mItems.push_back (std::make_pair (state, std::make_pair (id, slot))); } } From 2193977eec52e51981ee7799de03123caabfeadc Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 22:59:18 +0200 Subject: [PATCH 25/33] Savegame: Don't fill CustomData from ESM records if the savegame overwrites it anyway This gets rid of some junk in ContainerStores (since clear() only sets count to 0 and doesn't really delete references), and significantly speeds up loading savegames (by about 80% in my test) --- apps/openmw/mwclass/container.cpp | 7 ++++++- apps/openmw/mwclass/creature.cpp | 15 ++++++++++++++- apps/openmw/mwclass/npc.cpp | 7 ++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 9498ea52d..53add4274 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -286,7 +286,12 @@ namespace MWClass { const ESM::ContainerState& state2 = dynamic_cast (state); - ensureCustomData (ptr); + if (!ptr.getRefData().getCustomData()) + { + // Create a CustomData, but don't fill it from ESM records (not needed) + std::auto_ptr data (new ContainerCustomData); + ptr.getRefData().setCustomData (data.release()); + } dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. readState (state2.mInventory); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 59c1087f9..97cf30fe1 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -798,7 +798,20 @@ namespace MWClass { const ESM::CreatureState& state2 = dynamic_cast (state); - ensureCustomData (ptr); + if (!ptr.getRefData().getCustomData()) + { + // Create a CustomData, but don't fill it from ESM records (not needed) + std::auto_ptr data (new CreatureCustomData); + + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::Creature::Weapon) + data->mContainerStore = new MWWorld::InventoryStore(); + else + data->mContainerStore = new MWWorld::ContainerStore(); + + ptr.getRefData().setCustomData (data.release()); + } CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a93cae9ba..58a5bc622 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1293,7 +1293,12 @@ namespace MWClass { const ESM::NpcState& state2 = dynamic_cast (state); - ensureCustomData (ptr); + if (!ptr.getRefData().getCustomData()) + { + // Create a CustomData, but don't fill it from ESM records (not needed) + std::auto_ptr data (new NpcCustomData); + ptr.getRefData().setCustomData (data.release()); + } NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); From c3e4160a0ae94dda942f483850b3f267e8f7665a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 23:50:55 +0200 Subject: [PATCH 26/33] Don't crash in StatsWindow for invalid faction rank values PcRaiseRank: don't allow to raise rank beyond max rank --- apps/openmw/mwgui/statswindow.cpp | 10 ++++++---- apps/openmw/mwscript/statsextensions.cpp | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index b4bd0d738..6c4e00ae5 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -488,14 +488,16 @@ namespace MWGui text += "\n#BF9959#{sExpelled}"; else { - text += std::string("\n#BF9959") + faction->mRanks[it->second]; + int rank = it->second; + rank = std::max(0, std::min(9, rank)); + text += std::string("\n#BF9959") + faction->mRanks[rank]; - if (it->second < 9) + if (rank < 9) { // player doesn't have max rank yet - text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; + text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[rank+1]; - ESM::RankData rankData = faction->mData.mRankData[it->second+1]; + ESM::RankData rankData = faction->mData.mRankData[rank+1]; const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index a3bc223aa..5a0cd8ae6 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -587,7 +587,9 @@ namespace MWScript } else { - player.getClass().getNpcStats(player).getFactionRanks()[factionID] = player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1; + player.getClass().getNpcStats(player).getFactionRanks()[factionID] = + std::min(player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1, + 9); } } } From d878456d0f1c77a6fe3c6831e3bc10e00cf32e8d Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 01:10:33 +0200 Subject: [PATCH 27/33] Don't add an extra path separator --- apps/openmw/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index b082ff908..a061cf63c 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -335,7 +335,7 @@ int main(int argc, char**argv) if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached()) { int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; - cc_install_handlers(argc, argv, 5, s, std::string(cfgMgr.getLogPath().string() + "/crash.log").c_str(), NULL); + cc_install_handlers(argc, argv, 5, s, (cfgMgr.getLogPath() / "crash.log").string().c_str(), NULL); std::cout << "Installing crash catcher" << std::endl; } else From 4234c70232f80e46bcb72000c976989fd7cfaae0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 02:00:40 +0200 Subject: [PATCH 28/33] Savegame: Disable CustomData load optimization for npcs and creatures for now to preserve compatibility (still enabled for containers) --- apps/openmw/mwclass/creature.cpp | 7 +++++++ apps/openmw/mwclass/npc.cpp | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 97cf30fe1..5dea2ed1a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -798,6 +798,12 @@ namespace MWClass { const ESM::CreatureState& state2 = dynamic_cast (state); + ensureCustomData(ptr); + + // If we do the following instead we get a sizable speedup, but this causes compatibility issues + // with 0.30 savegames, where some state in CreatureStats was not saved yet, + // and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release. + /* if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) @@ -812,6 +818,7 @@ namespace MWClass ptr.getRefData().setCustomData (data.release()); } + */ CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 58a5bc622..6b77f30e7 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1293,12 +1293,18 @@ namespace MWClass { const ESM::NpcState& state2 = dynamic_cast (state); + ensureCustomData(ptr); + // If we do the following instead we get a sizable speedup, but this causes compatibility issues + // with 0.30 savegames, where some state in CreatureStats was not saved yet, + // and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release. + /* if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) std::auto_ptr data (new NpcCustomData); ptr.getRefData().setCustomData (data.release()); } + */ NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); From 6760f4c897b083b71d412000ee534541c3e1be5a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 02:09:46 +0200 Subject: [PATCH 29/33] Make cached GMSTs in MWClass::Npc/Creature safer --- apps/openmw/mwclass/creature.cpp | 83 +++++++-------- apps/openmw/mwclass/creature.hpp | 31 +++--- apps/openmw/mwclass/npc.cpp | 175 ++++++++++++++----------------- apps/openmw/mwclass/npc.hpp | 47 +++++---- 4 files changed, 160 insertions(+), 176 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5dea2ed1a..4a159a773 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -59,35 +59,38 @@ namespace namespace MWClass { + const Creature::GMST& Creature::getGmst() + { + static GMST gmst; + static bool inited = false; + if (!inited) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &store = world->getStore().get(); + gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature"); + gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature"); + gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect"); + gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier"); + gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus"); + gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier"); + gmst.fMinFlySpeed = store.find("fMinFlySpeed"); + gmst.fMaxFlySpeed = store.find("fMaxFlySpeed"); + gmst.fSwimRunBase = store.find("fSwimRunBase"); + gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult"); + gmst.fKnockDownMult = store.find("fKnockDownMult"); + gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult"); + gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); + inited = true; + } + return gmst; + } + void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data (new CreatureCustomData); - static bool inited = false; - if(!inited) - { - const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); - - fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature"); - fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature"); - fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect"); - fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier"); - fAthleticsRunBonus = gmst.find("fAthleticsRunBonus"); - fBaseRunMultiplier = gmst.find("fBaseRunMultiplier"); - fMinFlySpeed = gmst.find("fMinFlySpeed"); - fMaxFlySpeed = gmst.find("fMaxFlySpeed"); - fSwimRunBase = gmst.find("fSwimRunBase"); - fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); - fKnockDownMult = gmst.find("fKnockDownMult"); - iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); - iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); - - inited = true; - } - MWWorld::LiveCellRef *ref = ptr.get(); // creature stats @@ -376,9 +379,9 @@ namespace MWClass if (damage > 0.f) { // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() - * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + * getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt(); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) { @@ -522,9 +525,10 @@ namespace MWClass float Creature::getSpeed(const MWWorld::Ptr &ptr) const { MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const GMST& gmst = getGmst(); - float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() - * (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat()); + float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat()); const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); @@ -534,7 +538,7 @@ namespace MWClass bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * - fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); + gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); float moveSpeed; if(normalizedEncumbrance >= 1.0f) @@ -544,8 +548,8 @@ namespace MWClass { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); - flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); - flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } @@ -555,8 +559,8 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; - swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * - fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * + gmst.fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; } else if(running) @@ -871,19 +875,4 @@ namespace MWClass MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); } - - const ESM::GameSetting* Creature::fMinWalkSpeedCreature; - const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; - const ESM::GameSetting *Creature::fEncumberedMoveEffect; - const ESM::GameSetting *Creature::fSneakSpeedMultiplier; - const ESM::GameSetting *Creature::fAthleticsRunBonus; - const ESM::GameSetting *Creature::fBaseRunMultiplier; - const ESM::GameSetting *Creature::fMinFlySpeed; - const ESM::GameSetting *Creature::fMaxFlySpeed; - const ESM::GameSetting *Creature::fSwimRunBase; - const ESM::GameSetting *Creature::fSwimRunAthleticsMult; - const ESM::GameSetting *Creature::fKnockDownMult; - const ESM::GameSetting *Creature::iKnockDownOddsMult; - const ESM::GameSetting *Creature::iKnockDownOddsBase; - } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 30573cd15..6920a4b1d 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -19,20 +19,25 @@ namespace MWClass static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name); - static const ESM::GameSetting *fMinWalkSpeedCreature; - static const ESM::GameSetting *fMaxWalkSpeedCreature; - static const ESM::GameSetting *fEncumberedMoveEffect; - static const ESM::GameSetting *fSneakSpeedMultiplier; - static const ESM::GameSetting *fAthleticsRunBonus; - static const ESM::GameSetting *fBaseRunMultiplier; - static const ESM::GameSetting *fMinFlySpeed; - static const ESM::GameSetting *fMaxFlySpeed; - static const ESM::GameSetting *fSwimRunBase; - static const ESM::GameSetting *fSwimRunAthleticsMult; - static const ESM::GameSetting *fKnockDownMult; - static const ESM::GameSetting *iKnockDownOddsMult; - static const ESM::GameSetting *iKnockDownOddsBase; + // cached GMSTs + struct GMST + { + const ESM::GameSetting *fMinWalkSpeedCreature; + const ESM::GameSetting *fMaxWalkSpeedCreature; + const ESM::GameSetting *fEncumberedMoveEffect; + const ESM::GameSetting *fSneakSpeedMultiplier; + const ESM::GameSetting *fAthleticsRunBonus; + const ESM::GameSetting *fBaseRunMultiplier; + const ESM::GameSetting *fMinFlySpeed; + const ESM::GameSetting *fMaxFlySpeed; + const ESM::GameSetting *fSwimRunBase; + const ESM::GameSetting *fSwimRunAthleticsMult; + const ESM::GameSetting *fKnockDownMult; + const ESM::GameSetting *iKnockDownOddsMult; + const ESM::GameSetting *iKnockDownOddsBase; + }; + static const GMST& getGmst(); public: diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6b77f30e7..c003e0b22 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -228,38 +228,44 @@ namespace namespace MWClass { - void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const + const Npc::GMST& Npc::getGmst() { + static GMST gmst; static bool inited = false; if(!inited) { const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); - fMinWalkSpeed = gmst.find("fMinWalkSpeed"); - fMaxWalkSpeed = gmst.find("fMaxWalkSpeed"); - fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect"); - fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier"); - fAthleticsRunBonus = gmst.find("fAthleticsRunBonus"); - fBaseRunMultiplier = gmst.find("fBaseRunMultiplier"); - fMinFlySpeed = gmst.find("fMinFlySpeed"); - fMaxFlySpeed = gmst.find("fMaxFlySpeed"); - fSwimRunBase = gmst.find("fSwimRunBase"); - fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); - fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase"); - fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier"); - fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase"); - fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); - fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); - fWereWolfRunMult = gmst.find("fWereWolfRunMult"); - fKnockDownMult = gmst.find("fKnockDownMult"); - iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); - iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); - fDamageStrengthBase = gmst.find("fDamageStrengthBase"); - fDamageStrengthMult = gmst.find("fDamageStrengthMult"); + gmst.fMinWalkSpeed = store.find("fMinWalkSpeed"); + gmst.fMaxWalkSpeed = store.find("fMaxWalkSpeed"); + gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect"); + gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier"); + gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus"); + gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier"); + gmst.fMinFlySpeed = store.find("fMinFlySpeed"); + gmst.fMaxFlySpeed = store.find("fMaxFlySpeed"); + gmst.fSwimRunBase = store.find("fSwimRunBase"); + gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult"); + gmst.fJumpEncumbranceBase = store.find("fJumpEncumbranceBase"); + gmst.fJumpEncumbranceMultiplier = store.find("fJumpEncumbranceMultiplier"); + gmst.fJumpAcrobaticsBase = store.find("fJumpAcrobaticsBase"); + gmst.fJumpAcroMultiplier = store.find("fJumpAcroMultiplier"); + gmst.fJumpRunMultiplier = store.find("fJumpRunMultiplier"); + gmst.fWereWolfRunMult = store.find("fWereWolfRunMult"); + gmst.fKnockDownMult = store.find("fKnockDownMult"); + gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult"); + gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); + gmst.fDamageStrengthBase = store.find("fDamageStrengthBase"); + gmst.fDamageStrengthMult = store.find("fDamageStrengthMult"); inited = true; } + return gmst; + } + + void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const + { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data(new NpcCustomData); @@ -404,11 +410,6 @@ namespace MWClass ptr.get(); assert(ref->mBase != NULL); - //std::string headID = ref->mBase->mHead; - - //int end = headID.find_last_of("head_") - 4; - //std::string bodyRaceID = headID.substr(0, end); - std::string model = "meshes\\base_anim.nif"; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); if(race->mData.mFlags & ESM::Race::Beast) @@ -423,9 +424,9 @@ namespace MWClass if(getNpcStats(ptr).isWerewolf()) { const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); - return gmst.find("sWerewolfPopup")->getString(); + return store.find("sWerewolfPopup")->getString(); } MWWorld::LiveCellRef *ref = ptr.get(); @@ -450,7 +451,9 @@ namespace MWClass void Npc::hit(const MWWorld::Ptr& ptr, int type) const { MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const GMST& gmst = getGmst(); + + const MWWorld::Store &store = world->getStore().get(); // Get the weapon used (if hand-to-hand, weapon = inv.end()) MWWorld::InventoryStore &inv = getInventoryStore(ptr); @@ -461,9 +464,9 @@ namespace MWClass // Reduce fatigue // somewhat of a guess, but using the weapon weight makes sense - const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); - const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); - const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); + const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat(); + const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat(); + const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat(); MWMechanics::DynamicStat fatigue = getCreatureStats(ptr).getFatigue(); const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; @@ -472,10 +475,10 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); getCreatureStats(ptr).setFatigue(fatigue); - const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); + const float fCombatDistance = store.find("fCombatDistance")->getFloat(); float dist = fCombatDistance * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : - gmst.find("fHandToHandReach")->getFloat()); + store.find("fHandToHandReach")->getFloat()); // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist); @@ -522,8 +525,8 @@ namespace MWClass if(attack) { damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); - damage *= fDamageStrengthBase->getFloat() + - (stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1); + damage *= gmst.fDamageStrengthBase->getFloat() + + (stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1); if(weaphashealth) { int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); @@ -535,7 +538,7 @@ namespace MWClass { // Reduce weapon charge by at least one, but cap at 0 weaphealth -= std::min(std::max(1, - (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth); + (int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth); weapon.getCellRef().setCharge(weaphealth); } @@ -552,8 +555,8 @@ namespace MWClass // Note: MCP contains an option to include Strength in hand-to-hand damage // calculations. Some mods recommend using it, so we may want to include am // option for it. - float minstrike = gmst.find("fMinHandToHandMult")->getFloat(); - float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat(); + float minstrike = store.find("fMinHandToHandMult")->getFloat(); + float maxstrike = store.find("fMaxHandToHandMult")->getFloat(); damage = stats.getSkill(weapskill).getModified(); damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); @@ -567,7 +570,7 @@ namespace MWClass damage *= glob.find("WerewolfClawMult")->mValue.getFloat(); } if(healthdmg) - damage *= gmst.find("fHandtoHandHealthPer")->getFloat(); + damage *= store.find("fHandtoHandHealthPer")->getFloat(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(stats.isWerewolf()) @@ -585,12 +588,12 @@ namespace MWClass bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); if(!detected) { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } if (othercls.getCreatureStats(victim).getKnockedDown()) - damage *= gmst.find("fCombatKODamageMult")->getFloat(); + damage *= store.find("fCombatKODamageMult")->getFloat(); // Apply "On hit" enchanted weapons std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; @@ -661,6 +664,8 @@ namespace MWClass // something, alert the character controller, scripts, etc. const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const GMST& gmst = getGmst(); + int chance = store.get().find("iVoiceHitOdds")->getInt(); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (roll < chance) @@ -669,9 +674,9 @@ namespace MWClass } // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() - * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + * gmst.iKnockDownOddsMult->getInt() * 0.01 + gmst.iKnockDownOddsBase->getInt(); roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) { @@ -870,6 +875,8 @@ namespace MWClass float Npc::getSpeed(const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); + const GMST& gmst = getGmst(); + const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); @@ -878,17 +885,17 @@ namespace MWClass bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); - float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* - (fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat()); - walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; + float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* + (gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat()); + walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed = std::max(0.0f, walkSpeed); if(sneaking) - walkSpeed *= fSneakSpeedMultiplier->getFloat(); + walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat(); float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * - fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); + gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); if(npcdata->mNpcStats.isWerewolf()) - runSpeed *= fWereWolfRunMult->getFloat(); + runSpeed *= gmst.fWereWolfRunMult->getFloat(); float moveSpeed; if(normalizedEncumbrance >= 1.0f) @@ -898,8 +905,8 @@ namespace MWClass { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); - flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); - flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } @@ -909,8 +916,8 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; - swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* - fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* + gmst.fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; } else if(running && !sneaking) @@ -926,9 +933,10 @@ namespace MWClass float Npc::getJump(const MWWorld::Ptr &ptr) const { const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const GMST& gmst = getGmst(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); - const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + - fJumpEncumbranceMultiplier->getFloat() * + const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() + + gmst.fJumpEncumbranceMultiplier->getFloat() * (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr)); float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified(); @@ -939,14 +947,14 @@ namespace MWClass a = 50.0f; } - float x = fJumpAcrobaticsBase->getFloat() + - std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); - x += 3.0f * b * fJumpAcroMultiplier->getFloat(); + float x = gmst.fJumpAcrobaticsBase->getFloat() + + std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat()); + x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat(); x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64; x *= encumbranceTerm; if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) - x *= fJumpRunMultiplier->getFloat(); + x *= gmst.fJumpRunMultiplier->getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); x -= -627.2f;/*gravity constant*/ x /= 3.0f; @@ -957,19 +965,19 @@ namespace MWClass float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const { MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); - const float fallDistanceMin = gmst.find("fFallDamageDistanceMin")->getFloat(); + const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat(); if (fallHeight >= fallDistanceMin) { const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude; - const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat(); - const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat(); - const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat(); - const float fallDistanceMult = gmst.find("fFallDistanceMult")->getFloat(); + const float fallAcroBase = store.find("fFallAcroBase")->getFloat(); + const float fallAcroMult = store.find("fFallAcroMult")->getFloat(); + const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat(); + const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat(); float x = fallHeight - fallDistanceMin; x -= (1.5 * acrobaticsSkill) + jumpSpellBonus; @@ -1104,14 +1112,14 @@ namespace MWClass float Npc::getArmorRating (const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); MWMechanics::NpcStats &stats = getNpcStats(ptr); MWWorld::InventoryStore &invStore = getInventoryStore(ptr); - int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); - float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); - float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); + int iBaseArmorSkill = store.find("iBaseArmorSkill")->getInt(); + float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); + float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); int ratings[MWWorld::InventoryStore::Slots]; @@ -1367,27 +1375,4 @@ namespace MWClass MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); } - - const ESM::GameSetting *Npc::fMinWalkSpeed; - const ESM::GameSetting *Npc::fMaxWalkSpeed; - const ESM::GameSetting *Npc::fEncumberedMoveEffect; - const ESM::GameSetting *Npc::fSneakSpeedMultiplier; - const ESM::GameSetting *Npc::fAthleticsRunBonus; - const ESM::GameSetting *Npc::fBaseRunMultiplier; - const ESM::GameSetting *Npc::fMinFlySpeed; - const ESM::GameSetting *Npc::fMaxFlySpeed; - const ESM::GameSetting *Npc::fSwimRunBase; - const ESM::GameSetting *Npc::fSwimRunAthleticsMult; - const ESM::GameSetting *Npc::fJumpEncumbranceBase; - const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier; - const ESM::GameSetting *Npc::fJumpAcrobaticsBase; - const ESM::GameSetting *Npc::fJumpAcroMultiplier; - const ESM::GameSetting *Npc::fJumpRunMultiplier; - const ESM::GameSetting *Npc::fWereWolfRunMult; - const ESM::GameSetting *Npc::fKnockDownMult; - const ESM::GameSetting *Npc::iKnockDownOddsMult; - const ESM::GameSetting *Npc::iKnockDownOddsBase; - const ESM::GameSetting *Npc::fDamageStrengthBase; - const ESM::GameSetting *Npc::fDamageStrengthMult; - } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 356e358b9..d6b1f8c26 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -17,27 +17,32 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; - static const ESM::GameSetting *fMinWalkSpeed; - static const ESM::GameSetting *fMaxWalkSpeed; - static const ESM::GameSetting *fEncumberedMoveEffect; - static const ESM::GameSetting *fSneakSpeedMultiplier; - static const ESM::GameSetting *fAthleticsRunBonus; - static const ESM::GameSetting *fBaseRunMultiplier; - static const ESM::GameSetting *fMinFlySpeed; - static const ESM::GameSetting *fMaxFlySpeed; - static const ESM::GameSetting *fSwimRunBase; - static const ESM::GameSetting *fSwimRunAthleticsMult; - static const ESM::GameSetting *fJumpEncumbranceBase; - static const ESM::GameSetting *fJumpEncumbranceMultiplier; - static const ESM::GameSetting *fJumpAcrobaticsBase; - static const ESM::GameSetting *fJumpAcroMultiplier; - static const ESM::GameSetting *fJumpRunMultiplier; - static const ESM::GameSetting *fWereWolfRunMult; - static const ESM::GameSetting *fKnockDownMult; - static const ESM::GameSetting *iKnockDownOddsMult; - static const ESM::GameSetting *iKnockDownOddsBase; - static const ESM::GameSetting *fDamageStrengthBase; - static const ESM::GameSetting *fDamageStrengthMult; + struct GMST + { + const ESM::GameSetting *fMinWalkSpeed; + const ESM::GameSetting *fMaxWalkSpeed; + const ESM::GameSetting *fEncumberedMoveEffect; + const ESM::GameSetting *fSneakSpeedMultiplier; + const ESM::GameSetting *fAthleticsRunBonus; + const ESM::GameSetting *fBaseRunMultiplier; + const ESM::GameSetting *fMinFlySpeed; + const ESM::GameSetting *fMaxFlySpeed; + const ESM::GameSetting *fSwimRunBase; + const ESM::GameSetting *fSwimRunAthleticsMult; + const ESM::GameSetting *fJumpEncumbranceBase; + const ESM::GameSetting *fJumpEncumbranceMultiplier; + const ESM::GameSetting *fJumpAcrobaticsBase; + const ESM::GameSetting *fJumpAcroMultiplier; + const ESM::GameSetting *fJumpRunMultiplier; + const ESM::GameSetting *fWereWolfRunMult; + const ESM::GameSetting *fKnockDownMult; + const ESM::GameSetting *iKnockDownOddsMult; + const ESM::GameSetting *iKnockDownOddsBase; + const ESM::GameSetting *fDamageStrengthBase; + const ESM::GameSetting *fDamageStrengthMult; + }; + + static const GMST& getGmst(); public: From cc3c6ae7b854601d74cbebf9c26e1178e230f62e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 02:33:44 +0200 Subject: [PATCH 30/33] Fix very slow movement on some creatures, e.g. rats (Bug #1136) Neither fAthleticsRunBonus, fBaseRunMultiplier or the creature's athletics skill (i.e. Combat stat) have any effect on the run speed (tested by setting those to absurd values). The new formula is just a guess and doesn't seem to be completely accurate. --- apps/openmw/mwclass/creature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4a159a773..1f286f7e6 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -537,8 +537,8 @@ namespace MWClass bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); - float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * - gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); + float runSpeed = walkSpeed*6; + runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this float moveSpeed; if(normalizedEncumbrance >= 1.0f) From 185ff279a3a19aa0b3a66fcc1cc5389d8adc62dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 03:29:36 +0200 Subject: [PATCH 31/33] Add missing sound effect for mages guild transport --- apps/openmw/mwgui/travelwindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index c16603554..9aa75173a 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -10,6 +10,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -140,6 +141,9 @@ namespace MWGui if (playerGoldisExterior()) + // Interior cell -> mages guild transport + MWBase::Environment::get().getSoundManager()->playSound("mysticism cast", 1, 1); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); From 4648524df43d4c8bed8195791b2323a05384412c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 04:53:25 +0200 Subject: [PATCH 32/33] Improve getLOS (use eye level). Also, don't crash when used with non-actors. --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwscript/aiextensions.cpp | 9 ++++++++- apps/openmw/mwworld/worldimp.cpp | 20 ++++++++++++-------- apps/openmw/mwworld/worldimp.hpp | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 29f821e85..f42edafd4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -407,7 +407,7 @@ namespace MWBase virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; ///< get all items in active cells owned by this Npc - virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0; + virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) = 0; ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0; diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 823d22a6a..9a3387a00 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -371,6 +371,12 @@ namespace MWScript MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); + if(!actor.getClass().isActor() || !observer.getClass().isActor()) + { + runtime.push(0); + return; + } + Interpreter::Type_Integer value = MWBase::Environment::get().getWorld()->getLOS(observer, actor) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer); @@ -392,9 +398,10 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true); bool value = false; - if(dest != MWWorld::Ptr() ) + if(dest != MWWorld::Ptr() && source.getClass().isActor() && dest.getClass().isActor()) { value = MWBase::Environment::get().getWorld()->getLOS(source,dest); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6e975d25a..bcae8c070 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2031,20 +2031,24 @@ namespace MWWorld } } - bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) + bool World::getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) { - if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled()) + if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled()) return false; // cannot get LOS unless both NPC's are enabled - Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); - const float* pos1 = npc.getRefData().getPosition().pos; - Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); - const float* pos2 = targetNpc.getRefData().getPosition().pos; + if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode()) + return false; // not in active cell - btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z); - btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z); + Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents(); + const float* pos1 = actor.getRefData().getPosition().pos; + Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle())->getHalfExtents(); + const float* pos2 = targetActor.getRefData().getPosition().pos; + + btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z*2*0.9); // eye level + btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z*2*0.9); std::pair result = mPhysEngine->rayTest(from, to,false); if(result.first == "") return true; + return false; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index db599b0de..8cb2ac18a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -482,7 +482,7 @@ namespace MWWorld virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out); ///< get all items in active cells owned by this Npc - virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc); + virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor); ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist); From c69a311ad83a9c4046a6131118738911d6e620f3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 04:57:46 +0200 Subject: [PATCH 33/33] Disable lighting for particles Fixes magic cast visuals being too dark depending on the environment. --- components/nifogre/material.cpp | 10 +++++++++- components/nifogre/material.hpp | 2 +- components/nifogre/ogrenifloader.cpp | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index 3a87e1d52..b1ae83107 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -106,7 +106,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents) + bool &needTangents, bool disableLighting) { Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialPtr material = matMgr.getByName(name); @@ -245,6 +245,14 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, } } + if (disableLighting) + { + ambient = Ogre::Vector3(0.f); + diffuse = Ogre::Vector3(0.f); + specular = Ogre::Vector3(0.f); + emissive = Ogre::Vector3(1.f); + } + { // Generate a hash out of all properties that can affect the material. size_t h = 0; diff --git a/components/nifogre/material.hpp b/components/nifogre/material.hpp index b02c7c236..890255dcc 100644 --- a/components/nifogre/material.hpp +++ b/components/nifogre/material.hpp @@ -49,7 +49,7 @@ public: const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents); + bool &needTangents, bool disableLighting=false); }; } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 36d750821..813c1660d 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -850,7 +850,10 @@ class NIFObjectLoader partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, texprop, matprop, alphaprop, vertprop, zprop, specprop, - wireprop, needTangents)); + wireprop, needTangents, + // MW doesn't light particles, but the MaterialProperty + // used still has lighting, so that must be ignored. + true)); partsys->setCullIndividually(false); partsys->setParticleQuota(particledata->numParticles);