From 8cdc7031f54f9ab92a0bb7cb8736cd3d3751f9fa Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Fri, 24 Jul 2020 21:47:49 +0200 Subject: [PATCH 1/5] Support vectors in settings.cfg --- apps/openmw/mwrender/viewovershoulder.cpp | 10 +++---- components/settings/settings.cpp | 36 +++++++++++++++++++++++ components/settings/settings.hpp | 6 ++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/viewovershoulder.cpp b/apps/openmw/mwrender/viewovershoulder.cpp index 4d708afe0a..39599bfea3 100644 --- a/apps/openmw/mwrender/viewovershoulder.cpp +++ b/apps/openmw/mwrender/viewovershoulder.cpp @@ -21,14 +21,14 @@ namespace MWRender mAutoSwitchShoulder(Settings::Manager::getBool("auto switch shoulder", "Camera")), mOverShoulderHorizontalOffset(30.f), mOverShoulderVerticalOffset(-10.f) { - std::stringstream offset(Settings::Manager::getString("view over shoulder offset", "Camera")); - offset >> mOverShoulderHorizontalOffset >> mOverShoulderVerticalOffset; - mDefaultShoulderIsRight = mOverShoulderHorizontalOffset >= 0; - mOverShoulderHorizontalOffset = std::abs(mOverShoulderHorizontalOffset); + osg::Vec2f offset = Settings::Manager::getVector2("view over shoulder offset", "Camera"); + mOverShoulderHorizontalOffset = std::abs(offset.x()); + mOverShoulderVerticalOffset = offset.y(); + mDefaultShoulderIsRight = offset.x() >= 0; mCamera->enableDynamicCameraDistance(true); mCamera->enableCrosshairInThirdPersonMode(true); - mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset}); + mCamera->setFocalPointTargetOffset(offset); } void ViewOverShoulderController::update() diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index 540af4d19f..b29dadcdc8 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -76,6 +76,28 @@ bool Manager::getBool (const std::string& setting, const std::string& category) return Misc::StringUtils::ciEqual(string, "true"); } +osg::Vec2f Manager::getVector2 (const std::string& setting, const std::string& category) +{ + const std::string& value = getString(setting, category); + std::stringstream stream(value); + float x, y; + stream >> x >> y; + if (stream.fail()) + throw std::runtime_error(std::string("Can't parse 2d vector: " + value)); + return osg::Vec2f(x, y); +} + +osg::Vec3f Manager::getVector3 (const std::string& setting, const std::string& category) +{ + const std::string& value = getString(setting, category); + std::stringstream stream(value); + float x, y, z; + stream >> x >> y >> z; + if (stream.fail()) + throw std::runtime_error(std::string("Can't parse 3d vector: " + value)); + return osg::Vec3f(x, y, z); +} + void Manager::setString(const std::string &setting, const std::string &category, const std::string &value) { CategorySettingValueMap::key_type key = std::make_pair(category, setting); @@ -111,6 +133,20 @@ void Manager::setBool(const std::string &setting, const std::string &category, c setString(setting, category, value ? "true" : "false"); } +void Manager::setVector2 (const std::string &setting, const std::string &category, const osg::Vec2f value) +{ + std::ostringstream stream; + stream << value.x() << " " << value.y(); + setString(setting, category, stream.str()); +} + +void Manager::setVector3 (const std::string &setting, const std::string &category, const osg::Vec3f value) +{ + std::ostringstream stream; + stream << value.x() << ' ' << value.y() << ' ' << value.z(); + setString(setting, category, stream.str()); +} + void Manager::resetPendingChange(const std::string &setting, const std::string &category) { CategorySettingValueMap::key_type key = std::make_pair(category, setting); diff --git a/components/settings/settings.hpp b/components/settings/settings.hpp index 17d237fc35..ecc5aa5fd3 100644 --- a/components/settings/settings.hpp +++ b/components/settings/settings.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include namespace Settings { @@ -44,11 +46,15 @@ namespace Settings static float getFloat (const std::string& setting, const std::string& category); static std::string getString (const std::string& setting, const std::string& category); static bool getBool (const std::string& setting, const std::string& category); + static osg::Vec2f getVector2 (const std::string& setting, const std::string& category); + static osg::Vec3f getVector3 (const std::string& setting, const std::string& category); static void setInt (const std::string& setting, const std::string& category, const int value); static void setFloat (const std::string& setting, const std::string& category, const float value); static void setString (const std::string& setting, const std::string& category, const std::string& value); static void setBool (const std::string& setting, const std::string& category, const bool value); + static void setVector2 (const std::string& setting, const std::string& category, const osg::Vec2f value); + static void setVector3 (const std::string& setting, const std::string& category, const osg::Vec3f value); }; } From e6036e13b92a46310cc9a387f73422dfb88f541d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 25 Jul 2020 15:54:49 +0400 Subject: [PATCH 2/5] Use more C++11 loops in game mechanics code --- apps/openmw/mwmechanics/aiavoiddoor.cpp | 15 +++-- apps/openmw/mwmechanics/aifollow.cpp | 4 +- apps/openmw/mwmechanics/aisequence.cpp | 46 ++++++------- apps/openmw/mwmechanics/aiwander.cpp | 6 +- apps/openmw/mwmechanics/autocalcspell.cpp | 82 ++++++++++------------- apps/openmw/mwmechanics/character.cpp | 6 +- apps/openmw/mwmechanics/levelledlist.hpp | 18 ++--- apps/openmw/mwmechanics/objects.cpp | 21 +++--- apps/openmw/mwmechanics/obstacle.cpp | 6 +- 9 files changed, 94 insertions(+), 110 deletions(-) diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 47f72efcec..ce25537564 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -60,13 +60,14 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont // Make all nearby actors also avoid the door std::vector actors; MWBase::Environment::get().getMechanicsManager()->getActorsInRange(pos.asVec3(),100,actors); - for(std::vector::iterator it = actors.begin(); it != actors.end(); ++it) { - if(*it != getPlayer()) { //Not the player - MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence(); - if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) { //Only add it once - seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it); - } - } + for(auto& actor : actors) + { + if (actor == getPlayer()) + continue; + + MWMechanics::AiSequence& seq = actor.getClass().getCreatureStats(actor).getAiSequence(); + if (seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) + seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr), actor); } return false; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index a9e43b3c3e..b3c308d75f 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -124,9 +124,9 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte followDistance = 313; short i = 0; followers.sort(); - for (std::list::iterator it = followers.begin(); it != followers.end(); ++it) + for (int followIndex : followers) { - if (*it == mFollowIndex) + if (followIndex == mFollowIndex) followDistance += 130 * i; ++i; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index f747b16f29..57d32898cc 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -406,36 +406,36 @@ void AiSequence::fill(const ESM::AIPackageList &list) if (!list.mList.empty() && list.mList.begin() != (list.mList.end()-1)) mRepeat = true; - for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) + for (const auto& esmPackage : list.mList) { std::unique_ptr package; - if (it->mType == ESM::AI_Wander) + if (esmPackage.mType == ESM::AI_Wander) { - ESM::AIWander data = it->mWander; + ESM::AIWander data = esmPackage.mWander; std::vector idles; idles.reserve(8); for (int i=0; i<8; ++i) idles.push_back(data.mIdle[i]); package = std::make_unique(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0); } - else if (it->mType == ESM::AI_Escort) + else if (esmPackage.mType == ESM::AI_Escort) { - ESM::AITarget data = it->mTarget; + ESM::AITarget data = esmPackage.mTarget; package = std::make_unique(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } - else if (it->mType == ESM::AI_Travel) + else if (esmPackage.mType == ESM::AI_Travel) { - ESM::AITravel data = it->mTravel; + ESM::AITravel data = esmPackage.mTravel; package = std::make_unique(data.mX, data.mY, data.mZ); } - else if (it->mType == ESM::AI_Activate) + else if (esmPackage.mType == ESM::AI_Activate) { - ESM::AIActivate data = it->mActivate; + ESM::AIActivate data = esmPackage.mActivate; package = std::make_unique(data.mName.toString()); } - else //if (it->mType == ESM::AI_Follow) + else //if (esmPackage.mType == ESM::AI_Follow) { - ESM::AITarget data = it->mTarget; + ESM::AITarget data = esmPackage.mTarget; package = std::make_unique(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } mPackages.push_back(std::move(package)); @@ -457,10 +457,9 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) // If there is more than one non-combat, non-pursue package in the list, enable repeating. int count = 0; - for (std::vector::const_iterator it = sequence.mPackages.begin(); - it != sequence.mPackages.end(); ++it) + for (auto& container : sequence.mPackages) { - if (isActualAiPackage(static_cast(it->mType))) + if (isActualAiPackage(static_cast(container.mType))) count++; } @@ -468,20 +467,19 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) mRepeat = true; // Load packages - for (std::vector::const_iterator it = sequence.mPackages.begin(); - it != sequence.mPackages.end(); ++it) + for (auto& container : sequence.mPackages) { std::unique_ptr package; - switch (it->mType) + switch (container.mType) { case ESM::AiSequence::Ai_Wander: { - package.reset(new AiWander(static_cast(it->mPackage))); + package.reset(new AiWander(static_cast(container.mPackage))); break; } case ESM::AiSequence::Ai_Travel: { - const auto source = static_cast(it->mPackage); + const auto source = static_cast(container.mPackage); if (source->mHidden) package.reset(new AiInternalTravel(source)); else @@ -490,27 +488,27 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) } case ESM::AiSequence::Ai_Escort: { - package.reset(new AiEscort(static_cast(it->mPackage))); + package.reset(new AiEscort(static_cast(container.mPackage))); break; } case ESM::AiSequence::Ai_Follow: { - package.reset(new AiFollow(static_cast(it->mPackage))); + package.reset(new AiFollow(static_cast(container.mPackage))); break; } case ESM::AiSequence::Ai_Activate: { - package.reset(new AiActivate(static_cast(it->mPackage))); + package.reset(new AiActivate(static_cast(container.mPackage))); break; } case ESM::AiSequence::Ai_Combat: { - package.reset(new AiCombat(static_cast(it->mPackage))); + package.reset(new AiCombat(static_cast(container.mPackage))); break; } case ESM::AiSequence::Ai_Pursue: { - package.reset(new AiPursue(static_cast(it->mPackage))); + package.reset(new AiPursue(static_cast(container.mPackage))); break; } default: diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 11c50dc096..9e179edebc 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -809,11 +809,11 @@ namespace MWMechanics void AiWander::AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex, AiWanderStorage& storage) { storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(npcPos)); - for (std::vector::const_iterator it = pathGrid->mEdges.begin(); it != pathGrid->mEdges.end(); ++it) + for (auto& edge : pathGrid->mEdges) { - if (it->mV0 == pointIndex) + if (edge.mV0 == pointIndex) { - AddPointBetweenPathGridPoints(pathGrid->mPoints[it->mV0], pathGrid->mPoints[it->mV1], storage); + AddPointBetweenPathGridPoints(pathGrid->mPoints[edge.mV0], pathGrid->mPoints[edge.mV1], storage); } } } diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 9cee1aa318..662cfe473b 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -61,38 +61,36 @@ namespace MWMechanics // Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the // Store must preserve the record ordering as it was in the content files. - for (MWWorld::Store::iterator iter = spells.begin(); iter != spells.end(); ++iter) + for (const ESM::Spell& spell : spells) { - const ESM::Spell* spell = &*iter; - - if (spell->mData.mType != ESM::Spell::ST_Spell) + if (spell.mData.mType != ESM::Spell::ST_Spell) continue; - if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc)) + if (!(spell.mData.mFlags & ESM::Spell::F_Autocalc)) continue; static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger(); - if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost) + if (baseMagicka < iAutoSpellTimesCanCast * spell.mData.mCost) continue; - if (race && race->mPowers.exists(spell->mId)) + if (race && race->mPowers.exists(spell.mId)) continue; - if (!attrSkillCheck(spell, actorSkills, actorAttributes)) + if (!attrSkillCheck(&spell, actorSkills, actorAttributes)) continue; int school; float skillTerm; - calcWeakestSchool(spell, actorSkills, school, skillTerm); + calcWeakestSchool(&spell, actorSkills, school, skillTerm); assert(school >= 0 && school < 6); SchoolCaps& cap = schoolCaps[school]; - if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost) + if (cap.mReachedLimit && spell.mData.mCost <= cap.mMinCost) continue; static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat(); - if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance) + if (calcAutoCastChance(&spell, actorSkills, actorAttributes, school) < fAutoSpellChance) continue; - selectedSpells.push_back(spell->mId); + selectedSpells.push_back(spell.mId); if (cap.mReachedLimit) { @@ -101,9 +99,9 @@ namespace MWMechanics selectedSpells.erase(found); cap.mMinCost = std::numeric_limits::max(); - for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) + for (const std::string& testSpellName : selectedSpells) { - const ESM::Spell* testSpell = spells.find(*weakIt); + const ESM::Spell* testSpell = spells.find(testSpellName); //int testSchool; //float dummySkillTerm; @@ -130,10 +128,10 @@ namespace MWMechanics if (cap.mCount == cap.mLimit) cap.mReachedLimit = true; - if (spell->mData.mCost < cap.mMinCost) + if (spell.mData.mCost < cap.mMinCost) { - cap.mWeakestSpell = spell->mId; - cap.mMinCost = spell->mData.mCost; + cap.mWeakestSpell = spell.mId; + cap.mMinCost = spell.mData.mCost; } } } @@ -154,32 +152,28 @@ namespace MWMechanics std::vector selectedSpells; - - const MWWorld::Store &spells = - esmStore.get(); - for (MWWorld::Store::iterator iter = spells.begin(); iter != spells.end(); ++iter) + const MWWorld::Store &spells = esmStore.get(); + for (const ESM::Spell& spell : spells) { - const ESM::Spell* spell = &*iter; - - if (spell->mData.mType != ESM::Spell::ST_Spell) + if (spell.mData.mType != ESM::Spell::ST_Spell) continue; - if (!(spell->mData.mFlags & ESM::Spell::F_PCStart)) + if (!(spell.mData.mFlags & ESM::Spell::F_PCStart)) continue; - if (reachedLimit && spell->mData.mCost <= minCost) + if (reachedLimit && spell.mData.mCost <= minCost) continue; - if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) + if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell.mId) != race->mPowers.mList.end()) continue; - if (baseMagicka < spell->mData.mCost) + if (baseMagicka < spell.mData.mCost) continue; static const float fAutoPCSpellChance = esmStore.get().find("fAutoPCSpellChance")->mValue.getFloat(); - if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance) + if (calcAutoCastChance(&spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance) continue; - if (!attrSkillCheck(spell, actorSkills, actorAttributes)) + if (!attrSkillCheck(&spell, actorSkills, actorAttributes)) continue; - selectedSpells.push_back(spell->mId); + selectedSpells.push_back(spell.mId); if (reachedLimit) { @@ -188,9 +182,9 @@ namespace MWMechanics selectedSpells.erase(it); minCost = std::numeric_limits::max(); - for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) + for (const std::string& testSpellName : selectedSpells) { - const ESM::Spell* testSpell = esmStore.get().find(*weakIt); + const ESM::Spell* testSpell = esmStore.get().find(testSpellName); if (testSpell->mData.mCost < minCost) { minCost = testSpell->mData.mCost; @@ -200,9 +194,9 @@ namespace MWMechanics } else { - if (spell->mData.mCost < minCost) + if (spell.mData.mCost < minCost) { - weakestSpell = spell; + weakestSpell = &spell; minCost = weakestSpell->mData.mCost; } static const unsigned int iAutoPCSpellMax = esmStore.get().find("iAutoPCSpellMax")->mValue.getInteger(); @@ -216,23 +210,22 @@ namespace MWMechanics bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes) { - const std::vector& effects = spell->mEffects.mList; - for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) + for (const auto& spellEffect : spell->mEffects.mList) { - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(spellEffect.mEffectID); static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get().find("iAutoSpellAttSkillMin")->mValue.getInteger(); if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)) { - assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length); - if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin) + assert (spellEffect.mSkill >= 0 && spellEffect.mSkill < ESM::Skill::Length); + if (actorSkills[spellEffect.mSkill] < iAutoSpellAttSkillMin) return false; } if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)) { - assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length); - if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin) + assert (spellEffect.mAttribute >= 0 && spellEffect.mAttribute < ESM::Attribute::Length); + if (actorAttributes[spellEffect.mAttribute] < iAutoSpellAttSkillMin) return false; } } @@ -244,11 +237,8 @@ namespace MWMechanics { // Morrowind for some reason uses a formula slightly different from magicka cost calculation float minChance = std::numeric_limits::max(); - - const ESM::EffectList& effects = spell->mEffects; - for (std::vector::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it) + for (const ESM::ENAMstruct& effect : spell->mEffects.mList) { - const ESM::ENAMstruct& effect = *it; const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); int minMagn = 1; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index aed6388955..c8e81aa491 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2653,11 +2653,11 @@ void CharacterController::updateContinuousVfx() std::vector effects; mAnimation->getLoopingEffects(effects); - for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) + for (int effectId : effects) { if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() - || mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(*it)).getMagnitude() <= 0) - mAnimation->removeEffect(*it); + || mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(effectId)).getMagnitude() <= 0) + mAnimation->removeEffect(effectId); } } diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index 697e2eda81..f716f068db 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -31,10 +31,10 @@ namespace MWMechanics std::vector candidates; int highestLevel = 0; - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + for (const auto& levelledItem : items) { - if (it->mLevel > highestLevel && it->mLevel <= playerLevel) - highestLevel = it->mLevel; + if (levelledItem.mLevel > highestLevel && levelledItem.mLevel <= playerLevel) + highestLevel = levelledItem.mLevel; } // For levelled creatures, the flags are swapped. This file format just makes so much sense. @@ -43,14 +43,14 @@ namespace MWMechanics allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels; std::pair highest = std::make_pair(-1, ""); - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + for (const auto& levelledItem : items) { - if (playerLevel >= it->mLevel - && (allLevels || it->mLevel == highestLevel)) + if (playerLevel >= levelledItem.mLevel + && (allLevels || levelledItem.mLevel == highestLevel)) { - candidates.push_back(it->mId); - if (it->mLevel >= highest.first) - highest = std::make_pair(it->mLevel, it->mId); + candidates.push_back(levelledItem.mId); + if (levelledItem.mLevel >= highest.first) + highest = std::make_pair(levelledItem.mLevel, levelledItem.mId); } } if (candidates.empty()) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 9e05509f1c..5b18fc2c30 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -19,11 +19,10 @@ Objects::Objects() Objects::~Objects() { - PtrControllerMap::iterator it(mObjects.begin()); - for (; it != mObjects.end();++it) + for(auto& object : mObjects) { - delete it->second; - it->second = nullptr; + delete object.second; + object.second = nullptr; } } @@ -77,8 +76,8 @@ void Objects::update(float duration, bool paused) { if(!paused) { - for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter) - iter->second->update(duration); + for(auto& object : mObjects) + object.second->update(duration); } else { @@ -87,15 +86,15 @@ void Objects::update(float duration, bool paused) if(mode != MWGui::GM_Container) return; - for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter) + for(auto& object : mObjects) { - if (iter->first.getTypeName() != typeid(ESM::Container).name()) + if (object.first.getTypeName() != typeid(ESM::Container).name()) continue; - if (iter->second->isAnimPlaying("containeropen")) + if (object.second->isAnimPlaying("containeropen")) { - iter->second->update(duration); - MWBase::Environment::get().getWorld()->updateAnimatedCollisionShape(iter->first); + object.second->update(duration); + MWBase::Environment::get().getWorld()->updateAnimatedCollisionShape(object.first); } } } diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index e30a2947fd..715dfecd24 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -36,17 +36,13 @@ namespace MWMechanics // Check all the doors in this cell const MWWorld::CellRefList& doors = cell->getReadOnlyDoors(); - const MWWorld::CellRefList::List& refList = doors.mList; - MWWorld::CellRefList::List::const_iterator it = refList.begin(); osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); pos.z() = 0; osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0)); - for (; it != refList.end(); ++it) + for (const auto& ref : doors.mList) { - const MWWorld::LiveCellRef& ref = *it; - osg::Vec3f doorPos(ref.mData.getPosition().asVec3()); // FIXME: cast From 2e27de027e642c7db06724fd54f2f7f6578e8092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Mocquillon?= Date: Sat, 25 Jul 2020 13:58:42 +0000 Subject: [PATCH 3/5] Add members in AdvancedPage class to avoid memory leak when the list of cells names is updated as we recreated a completer at each notification event --- CHANGELOG.md | 1 + apps/launcher/advancedpage.cpp | 12 ++++++------ apps/launcher/advancedpage.hpp | 4 ++++ components/contentselector/view/combobox.cpp | 1 + 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c98dfbe3df..e75b20dada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ Feature #5524: Resume failed script execution after reload Feature #5525: Search fields tweaks (utf-8) Task #5480: Drop Qt4 support + Task #5520: Improve cell name autocompleter implementation 0.46.0 ------ diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index e9db74cae1..00d0df0489 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -19,15 +19,15 @@ Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg, setupUi(this); loadSettings(); + mCellNameCompleter.setModel(&mCellNameCompleterModel); + startDefaultCharacterAtField->setCompleter(&mCellNameCompleter); } void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) { - // Set up an auto-completer for the "Start default character at" field - auto *completer = new QCompleter(cellNames); - completer->setCompletionMode(QCompleter::PopupCompletion); - completer->setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); - startDefaultCharacterAtField->setCompleter(completer); - + // Update the list of suggestions for the "Start default character at" field + mCellNameCompleterModel.setStringList(cellNames); + mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion); + mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); } void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) { diff --git a/apps/launcher/advancedpage.hpp b/apps/launcher/advancedpage.hpp index 3f5e5bfa7a..25cb66d9d8 100644 --- a/apps/launcher/advancedpage.hpp +++ b/apps/launcher/advancedpage.hpp @@ -2,6 +2,8 @@ #define ADVANCEDPAGE_H #include +#include +#include #include "ui_advancedpage.h" @@ -35,6 +37,8 @@ namespace Launcher Files::ConfigurationManager &mCfgMgr; Config::GameSettings &mGameSettings; Settings::Manager &mEngineSettings; + QCompleter mCellNameCompleter; + QStringListModel mCellNameCompleterModel; /** * Load the cells associated with the given content files for use in autocomplete diff --git a/components/contentselector/view/combobox.cpp b/components/contentselector/view/combobox.cpp index 959eca2890..1ef9f9bd75 100644 --- a/components/contentselector/view/combobox.cpp +++ b/components/contentselector/view/combobox.cpp @@ -8,6 +8,7 @@ ContentSelectorView::ComboBox::ComboBox(QWidget *parent) : { mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore setValidator(mValidator); + setEditable(true); setCompleter(0); setEnabled (true); From 1f4f10c72321e4c7f5023edeb3ac630f976d1fe0 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Thu, 9 Jul 2020 14:56:13 +0200 Subject: [PATCH 4/5] Add a tab level in advanced settings --- apps/launcher/advancedpage.cpp | 337 ++++-- .../reference/modding/settings/camera.rst | 2 +- .../reference/modding/settings/game.rst | 2 +- files/ui/advancedpage.ui | 1073 +++++++++-------- 4 files changed, 791 insertions(+), 623 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 00d0df0489..07fe8ddd6a 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -4,9 +4,43 @@ #include #include #include +#include #include #include +#include + + +class HorizontalTextWestTabStyle : public QProxyStyle +{ +public: + QSize sizeFromContents(ContentsType type, const QStyleOption* option, const QSize& size, const QWidget* widget) const + { + QSize s = QProxyStyle::sizeFromContents(type, option, size, widget); + if (type == QStyle::CT_TabBarTab) + { + s.transpose(); + s.setHeight(s.height() + 20); + } + return s; + } + + void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const + { + if (element == CE_TabBarTabLabel) + { + if (const QStyleOptionTab* tab = qstyleoption_cast(option)) + { + QStyleOptionTab opt(*tab); + opt.shape = QTabBar::RoundedNorth; + QProxyStyle::drawControl(element, &opt, painter, widget); + return; + } + } + QProxyStyle::drawControl(element, option, painter, widget); + } +}; + Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Settings::Manager &engineSettings, QWidget *parent) @@ -19,6 +53,7 @@ Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg, setupUi(this); loadSettings(); + AdvancedTabWidget->tabBar()->setStyle(new HorizontalTextWestTabStyle); mCellNameCompleter.setModel(&mCellNameCompleterModel); startDefaultCharacterAtField->setCompleter(&mCellNameCompleter); } @@ -55,142 +90,220 @@ void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked() runScriptAfterStartupField->setText(path); } +namespace +{ + constexpr double CellSizeInUnits = 8192; + + double convertToCells(double unitRadius) + { + return std::round((unitRadius / 0.93 + 1024) / CellSizeInUnits); + } + + double convertToUnits(double CellGridRadius) + { + return (CellSizeInUnits * CellGridRadius - 1024) * 0.93; + } +} + bool Launcher::AdvancedPage::loadSettings() { - // Testing - bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1; - if (skipMenu) { - skipMenuCheckBox->setCheckState(Qt::Checked); - } - startDefaultCharacterAtLabel->setEnabled(skipMenu); - startDefaultCharacterAtField->setEnabled(skipMenu); - - startDefaultCharacterAtField->setText(mGameSettings.value("start")); - runScriptAfterStartupField->setText(mGameSettings.value("script-run")); - - // Game Settings - loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); - loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); - loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); - loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); - loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); - loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); - loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); - if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) - unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); - loadSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); - loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); - loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); - connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool))); - loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); - if (animSourcesCheckBox->checkState()) + // Game mechanics { - loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); - loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); + loadSettingBool(toggleSneakCheckBox, "toggle sneak", "Input"); + loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); + loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); + loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); + loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); + loadSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); + loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); + loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); + int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); + if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) + unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); } - loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); - loadSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); - // Input Settings - loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); - loadSettingBool(toggleSneakCheckBox, "toggle sneak", "Input"); + // Visuals + { + loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); + loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); + connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool))); + loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); + if (animSourcesCheckBox->checkState()) + { + loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); + loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); + } + loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera"); + loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); - // Saves Settings - loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); - maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves")); + const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain"); + const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain"); + if (distantTerrain && objectPaging) { + distantLandCheckBox->setCheckState(Qt::Checked); + } - // User Interface Settings - loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); - loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); - loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); - loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); - loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); - int showOwnedIndex = mEngineSettings.getInt("show owned", "Game"); - // Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid. - if (showOwnedIndex >= 0 && showOwnedIndex <= 3) - showOwnedComboBox->setCurrentIndex(showOwnedIndex); + loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); + viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera"))); + } - // Other Settings - QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper(); - if (screenshotFormatComboBox->findText(screenshotFormatString) == -1) - screenshotFormatComboBox->addItem(screenshotFormatString); - screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString)); + // Interface Changes + { + loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); + loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); + loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); + loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); + loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); + int showOwnedIndex = mEngineSettings.getInt("show owned", "Game"); + // Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid. + if (showOwnedIndex >= 0 && showOwnedIndex <= 3) + showOwnedComboBox->setCurrentIndex(showOwnedIndex); + } + // Bug fixes + { + loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); + loadSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); + } + + // Miscellaneous + { + // Saves + loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); + maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves")); + + // Other Settings + QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper(); + if (screenshotFormatComboBox->findText(screenshotFormatString) == -1) + screenshotFormatComboBox->addItem(screenshotFormatString); + screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString)); + } + + // Testing + { + loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); + + bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1; + if (skipMenu) + { + skipMenuCheckBox->setCheckState(Qt::Checked); + } + startDefaultCharacterAtLabel->setEnabled(skipMenu); + startDefaultCharacterAtField->setEnabled(skipMenu); + + startDefaultCharacterAtField->setText(mGameSettings.value("start")); + runScriptAfterStartupField->setText(mGameSettings.value("script-run")); + } return true; } void Launcher::AdvancedPage::saveSettings() { - // Ensure we only set the new settings if they changed. This is to avoid cluttering the - // user settings file (which by definition should only contain settings the user has touched) + // Game mechanics + { + saveSettingBool(toggleSneakCheckBox, "toggle sneak", "Input"); + saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); + saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); + saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); + saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); + saveSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); + saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); + saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); + int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); + if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) + mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); + } + + // Visuals + { + saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); + saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); + saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); + saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); + saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); + saveSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera"); + saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); + + const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain"); + const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain"); + const bool wantDistantLand = distantLandCheckBox->checkState(); + if (wantDistantLand != (distantTerrain && objectPaging)) { + mEngineSettings.setBool("distant terrain", "Terrain", wantDistantLand); + mEngineSettings.setBool("object paging", "Terrain", wantDistantLand); + } + + saveSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); + double viewingDistance = viewingDistanceComboBox->value(); + if (viewingDistance != convertToCells(mEngineSettings.getInt("viewing distance", "Camera"))) + { + mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance)); + } + } + + // Interface Changes + { + saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); + saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); + saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); + saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); + saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); + int showOwnedCurrentIndex = showOwnedComboBox->currentIndex(); + if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game")) + mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex); + } + + // Bug fixes + { + saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); + saveSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); + } + + // Miscellaneous + { + // Saves Settings + saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); + int maximumQuicksaves = maximumQuicksavesComboBox->value(); + if (maximumQuicksaves != mEngineSettings.getInt("max quicksaves", "Saves")) + { + mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves); + } + + // Other Settings + std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); + if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General")) + mEngineSettings.setString("screenshot format", "General", screenshotFormatString); + } // Testing - int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked; - if (skipMenu != mGameSettings.value("skip-menu").toInt()) - mGameSettings.setValue("skip-menu", QString::number(skipMenu)); + { + saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); - QString startCell = startDefaultCharacterAtField->text(); - if (startCell != mGameSettings.value("start")) { - mGameSettings.setValue("start", startCell); + int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked; + if (skipMenu != mGameSettings.value("skip-menu").toInt()) + mGameSettings.setValue("skip-menu", QString::number(skipMenu)); + + QString startCell = startDefaultCharacterAtField->text(); + if (startCell != mGameSettings.value("start")) + { + mGameSettings.setValue("start", startCell); + } + QString scriptRun = runScriptAfterStartupField->text(); + if (scriptRun != mGameSettings.value("script-run")) + mGameSettings.setValue("script-run", scriptRun); } - QString scriptRun = runScriptAfterStartupField->text(); - if (scriptRun != mGameSettings.value("script-run")) - mGameSettings.setValue("script-run", scriptRun); - - // Game Settings - saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); - saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); - saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); - saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); - saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); - saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); - saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); - if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) - mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); - saveSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); - saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); - saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); - saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); - saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); - saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); - saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); - saveSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); - - // Input Settings - saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); - saveSettingBool(toggleSneakCheckBox, "toggle sneak", "Input"); - - // Saves Settings - saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); - int maximumQuicksaves = maximumQuicksavesComboBox->value(); - if (maximumQuicksaves != mEngineSettings.getInt("max quicksaves", "Saves")) { - mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves); - } - - // User Interface Settings - saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); - saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); - saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); - saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); - saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); - int showOwnedCurrentIndex = showOwnedComboBox->currentIndex(); - if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game")) - mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex); - - // Other Settings - std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); - if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General")) - mEngineSettings.setString("screenshot format", "General", screenshotFormatString); } -void Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) { +void Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) +{ if (mEngineSettings.getBool(setting, group)) checkbox->setCheckState(Qt::Checked); } -void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) { +void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) +{ bool cValue = checkbox->checkState(); if (cValue != mEngineSettings.getBool(setting, group)) mEngineSettings.setBool(setting, group, cValue); diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index 18b6754a74..8d70789052 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -136,7 +136,7 @@ This setting controls third person view mode. False: View is centered on the character's head. Crosshair is hidden. True: In non-combat mode camera is positioned behind the character's shoulder. Crosshair is visible in third person mode as well. -This setting can only be configured by editing the settings configuration file. +This setting can be controlled in Advanced tab of the launcher. view over shoulder offset ------------------------- diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 8d0b0dfc14..072c614d4d 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -329,7 +329,7 @@ If disabled then the whole character's body is pointed to the direction of view. If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it also changes straight right and straight left movement. -This setting can only be configured by editing the settings configuration file. +This setting can be controlled in Advanced tab of the launcher. swim upward coef ---------------- diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 142cd72594..de8e3dfc01 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -4,280 +4,466 @@ - - - true + + + QTabWidget::West - + + 0 + + + + Game mechanics + + + + + + <html><head/><body><p>This setting causes the behavior of the sneak key (bound to Ctrl by default) to toggle sneaking on and off rather than requiring the key to be held down while sneaking. Players that spend significant time sneaking may find the character easier to control with this option enabled. </p></body></html> + + + Toggle sneak + + + + + + + <html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html> + + + Can loot during death animation + + + + + + + <html><head/><body><p>Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.</p></body></html> + + + Followers defend immediately + + + + + + + <html><head/><body><p>Make the value of filled soul gems dependent only on soul magnitude.</p></body></html> + + + Soulgem values rebalance + + + + + + + <html><head/><body><p>Make enchanted weaponry without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html> + + + Enchanted weapons are magical + + + + + + + <html><head/><body><p>Make disposition change of merchants caused by trading permanent.</p></body></html> + + + Permanent barter disposition changes + + + + + + + <html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html> + + + Classic reflected Absorb spells behavior + + + + + + + <html><head/><body><p>Allow non-standard ammunition solely to bypass normal weapon resistance or weakness.</p></body></html> + + + Only appropriate ammunition bypasses normal weapon resistance + + + + + + + <html><head/><body><p>Make Damage Fatigue magic effect uncapped like Drain Fatigue effect.</p><p>This means that unlike Morrowind you will be able to knock down actors using this effect.</p></body></html> + + + Uncapped Damage Fatigue + + + + + + + <html><head/><body><p>Don't use race weight in NPC movement speed calculations.</p></body></html> + + + Racial variation in speed fix + + + + + + + <html><head/><body><p>Factor strength into hand-to-hand damage calculations, as the MCP formula: damage * (strength / 40).</p><p>The default value is Off.</p></body></html> + + + + + + Factor strength into hand-to-hand combat: + + + + + + + 0 + + + + Off + + + + + Affect werewolves + + + + + Do not affect werewolves + + + + + + + + + + + Qt::Vertical + + + + + + + + Visuals + + + + + + <html><head/><body><p>Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. +Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. +Affected objects will use shaders. +</p></body></html> + + + Bump/reflect map local lighting + + + + + + + <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> + + + Use magic item animation + + + + + + + <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> + + + Use additional animation sources + + + + + + + + 20 + + + + + false + + + <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> + + + Weapon sheathing + + + + + + + false + + + <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> + + + Shield sheathing + + + + + + + + + + <html><head/><body><p>This setting controls third person view mode.</p><p>False: View is centered on the character's head. Crosshair is hidden. +True: In non-combat mode camera is positioned behind the character's shoulder. Crosshair is visible in third person mode as well. +</p></body></html> + + + View over the shoulder + + + + + + + <html><head/><body><p>Affects side and diagonal movement. Enabling this setting makes movement more realistic.</p><p>If disabled then the whole character's body is pointed to the direction of view. Diagonal movement has no special animation and causes sliding.</p><p>If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it also changes straight right and straight left movement.</p></body></html> + + + Turn to movement direction + + + + + + + <html><head/><body><p>If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.</p></body></html> + + + Distant land + + + + + + + <html><head/><body><p>Use object paging for active cells grid.</p></body></html> + + + Active grid object paging + + + + + + + <html><head/><body><p>This value controls the maximum visible distance (in cell units). +Larger values significantly improve rendering in exterior spaces, +but also increase the amount of rendered geometry and significantly reduce the frame rate.</p></body></html> + + + + + + Viewing distance + + + + + + + 0.0 + + + 0.5 + + + Cells + + + + + + + + + + Qt::Vertical + + + + + + + + Interface changes + + + + + + <html><head/><body><p>Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. </p><p>The default value is false.</p></body></html> + + + Show effect duration + + + + + + + <html><head/><body><p>Whether or not the chance of success will be displayed in the enchanting menu.</p><p>The default value is false.</p></body></html> + + + Show enchant chance + + + + + + + <html><head/><body><p>If this setting is true, melee weapons reach and speed will be shown on item tooltip.</p><p>The default value is false.</p></body></html> + + + Show melee info + + + + + + + <html><head/><body><p>If this setting is true, damage bonus of arrows and bolts will be shown on item tooltip.</p><p>The default value is false.</p></body></html> + + + Show projectile damage + + + + + + + <html><head/><body><p>If this setting is true, dialogue topics will have a different color if the topic is specific to the NPC you're talking to or the topic was previously seen. Color can be changed in settings.cfg.</p><p>The default value is false.</p></body></html> + + + Change dialogue topic color + + + + + + + <html><head/><body><p>Enable visual clues for items owned by NPCs when the crosshair is on the object.</p><p>The default value is Off.</p></body></html> + + + + + + Show owned: + + + + + + + 1 + + + + Off + + + + + Tool Tip Only + + + + + Crosshair Only + + + + + Tool Tip and Crosshair + + + + + + + + + + + Qt::Vertical + + + + + + + + Bug fixes + + + + + + <html><head/><body><p>Prevents merchants from equipping items that are sold to them.</p></body></html> + + + Merchant equipping fix + + + + + + + <html><head/><body><p>Trainers now only choose which skills to train using their base skill points, allowing mercantile improving effects to be used without making mercantile an offered skill.</p></body></html> + + + Trainers choose their training skills based on their base skill points + + + + + + + Qt::Vertical + + + + + + + + Miscellaneous + - - - - Game Mechanics - - - - - - <html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html> - - - Can loot during death animation - - - - - - - <html><head/><body><p>Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.</p></body></html> - - - Followers attack on sight - - - - - - - <html><head/><body><p>Prevents merchants from equipping items that are sold to them.</p></body></html> - - - Prevent merchant equipping - - - - - - - <html><head/><body><p>Make the value of filled soul gems dependent only on soul magnitude.</p></body></html> - - - Rebalance soul gem values - - - - - - - <html><head/><body><p>Make enchanted weaponry without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html> - - - Enchanted weapons are magical - - - - - - - <html><head/><body><p>Make disposition change of merchants caused by trading permanent.</p></body></html> - - - Barter disposition change is permanent - - - - - - - <html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html> - - - Classic reflected Absorb spells behavior - - - - - - - <html><head/><body><p>Allow non-standard ammunition solely to bypass normal weapon resistance or weakness.</p></body></html> - - - Only appropriate ammunition bypasses normal weapon resistance - - - - - - - <html><head/><body><p>Factor strength into hand-to-hand damage calculations, as the MCP formula: damage * (strength / 40).</p><p>The default value is Off.</p></body></html> - - - - -1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Factor strength into hand-to-hand combat: - - - - - - - 0 - - - - Off - - - - - Affect werewolves - - - - - Do not affect werewolves - - - - - - - - - - - <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> - - - Use magic item animation - - - - - - - <html><head/><body><p>Don't use race weight in NPC movement speed calculations.</p></body></html> - - - Normalise race speed - - - - - - - <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> - - - Use additional animation sources - - - - - - - - 6 - - - 20 - - - 0 - - - 0 - - - 0 - - - - - false - - - <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> - - - Weapon sheathing - - - - - - - false - - - <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> - - - Shield sheathing - - - - - - - - - - <html><head/><body><p>Make Damage Fatigue magic effect uncapped like Drain Fatigue effect.</p><p>This means that unlike Morrowind you will be able to knock down actors using this effect.</p></body></html> - - - Uncapped Damage Fatigue - - - - - - - <html><head/><body><p>Trainers now only choose which skills to train using their base skill points, allowing mercantile improving effects to be used without making mercantile an offered skill.</p></body></html> - - - Trainers choose their training skills based on their base skill points - - - - - - - - - - Input - - - - - - <html><head/><body><p>OpenMW will capture control of the cursor if this setting is true.</p><p>In “look mode”, OpenMW will center the cursor regardless of the value of this setting (since the cursor/crosshair is always centered in the OpenMW window). However, in GUI mode, this setting determines the behavior when the cursor is moved outside the OpenMW window. If true, the cursor movement stops at the edge of the window preventing access to other applications. If false, the cursor is allowed to move freely on the desktop.</p><p>This setting does not apply to the screen where escape has been pressed, where the cursor is never captured. Regardless of this setting “Alt-Tab” or some other operating system dependent key sequence can be used to allow the operating system to regain control of the mouse cursor. This setting interacts with the minimize on focus loss setting by affecting what counts as a focus loss. Specifically on a two-screen configuration it may be more convenient to access the second screen with setting disabled.</p><p>Note for developers: it’s desirable to have this setting disabled when running the game in a debugger, to prevent the mouse cursor from becoming unusable when the game pauses on a breakpoint.</p></body></html> - - - Grab cursor - - - - - - - <html><head/><body><p>This setting causes the behavior of the sneak key (bound to Ctrl by default) to toggle sneaking on and off rather than requiring the key to be held down while sneaking. Players that spend significant time sneaking may find the character easier to control with this option enabled. </p></body></html> - - - Toggle sneak - - - - - - @@ -295,26 +481,11 @@ - + <html><head/><body><p>This setting determines how many quicksave and autosave slots you can have at a time. If greater than 1, quicksaves will be sequentially created each time you quicksave. Once the maximum number of quicksaves has been reached, the oldest quicksave will be recycled the next time you perform a quicksave.</p></body></html> - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - @@ -335,211 +506,6 @@ - - - - Testing - - - - - - These settings are intended for testing mods and will cause issues if used for normal gameplay. - - - true - - - - - - - Qt::Horizontal - - - - - - - Skip menu and generate default character - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - Start default character at - - - - - - - default cell - - - - - - - - - Run script after startup: - - - - - - - - - - - - Browse… - - - - - - - - - - - - User Interface - - - - - - <html><head/><body><p>Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. </p><p>The default value is false.</p></body></html> - - - Show effect duration - - - - - - - <html><head/><body><p>Whether or not the chance of success will be displayed in the enchanting menu.</p><p>The default value is false.</p></body></html> - - - Show enchant chance - - - - - - - <html><head/><body><p>If this setting is true, melee weapons reach and speed will be shown on item tooltip.</p><p>The default value is false.</p></body></html> - - - Show melee info - - - - - - - <html><head/><body><p>If this setting is true, damage bonus of arrows and bolts will be shown on item tooltip.</p><p>The default value is false.</p></body></html> - - - Show projectile damage - - - - - - - <html><head/><body><p>If this setting is true, dialogue topics will have a different color if the topic is specific to the NPC you're talking to or the topic was previously seen. Color can be changed in settings.cfg.</p><p>The default value is false.</p></body></html> - - - Change dialogue topic color - - - - - - - <html><head/><body><p>Enable visual clues for items owned by NPCs when the crosshair is on the object.</p><p>The default value is Off.</p></body></html> - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Show owned: - - - - - - - 1 - - - - Off - - - - - Tool Tip Only - - - - - Crosshair Only - - - - - Tool Tip and Crosshair - - - - - - - - - - @@ -547,26 +513,11 @@ - + <html><head/><body><p>Specify the format for screen shots taken by pressing the screen shot key (bound to F12 by default). This setting should be the file extension commonly associated with the desired format. The formats supported will be determined at compilation, but “jpg”, “png”, and “tga” should be allowed.</p></body></html> - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - @@ -599,6 +550,110 @@ + + + + Qt::Vertical + + + + + + + + Testing + + + + + + These settings are intended for testing mods and will cause issues if used for normal gameplay. + + + true + + + + + + + Qt::Horizontal + + + + + + + <html><head/><body><p>OpenMW will capture control of the cursor if this setting is true.</p><p>In “look mode”, OpenMW will center the cursor regardless of the value of this setting (since the cursor/crosshair is always centered in the OpenMW window). However, in GUI mode, this setting determines the behavior when the cursor is moved outside the OpenMW window. If true, the cursor movement stops at the edge of the window preventing access to other applications. If false, the cursor is allowed to move freely on the desktop.</p><p>This setting does not apply to the screen where escape has been pressed, where the cursor is never captured. Regardless of this setting “Alt-Tab” or some other operating system dependent key sequence can be used to allow the operating system to regain control of the mouse cursor. This setting interacts with the minimize on focus loss setting by affecting what counts as a focus loss. Specifically on a two-screen configuration it may be more convenient to access the second screen with setting disabled.</p><p>Note for developers: it’s desirable to have this setting disabled when running the game in a debugger, to prevent the mouse cursor from becoming unusable when the game pauses on a breakpoint.</p></body></html> + + + Grab cursor + + + + + + + Skip menu and generate default character + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + + + + Start default character at + + + + + + + default cell + + + + + + + + + Run script after startup: + + + + + + + + + + + + Browse… + + + + + + + + + Qt::Vertical + + + From 1ff2256bc1899cfdd9345027568c5aff8d573fa9 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Thu, 16 Jul 2020 16:17:25 +0200 Subject: [PATCH 5/5] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e75b20dada..6fe149c7ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Feature #5445: Handle NiLines Feature #5457: Realistic diagonal movement Feature #5486: Fixes trainers to choose their training skills based on their base skill points + Feature #5519: Code Patch tab in launcher Feature #5524: Resume failed script execution after reload Feature #5525: Search fields tweaks (utf-8) Task #5480: Drop Qt4 support