diff --git a/.travis.yml b/.travis.yml index 781f498e2..6d8970b67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,4 @@ -os: - - linux - - osx -osx_image: xcode9.4 language: cpp -sudo: required -dist: trusty branches: only: - master @@ -20,16 +14,16 @@ addons: sources: - sourceline: 'ppa:openmw/openmw' - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.6 + - llvm-toolchain-trusty-7 packages: [ # Dev - cmake, clang-3.6, libunshield-dev, libtinyxml-dev, + cmake, clang-7, clang-tools-7, gcc-8, g++-8, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev, - # Audio & Video - libsdl2-dev, libqt4-dev, libopenal-dev, + # Audio, Video and Misc. deps + libsdl2-dev, libqt4-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, # The other ones from OpenMW ppa libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev ] @@ -44,18 +38,44 @@ addons: branch_pattern: coverity_scan matrix: include: - - os: linux + - name: OpenMW (all) on MacOS xcode9.4 + os: osx + osx_image: xcode9.4 + - name: OpenMW (all) on Ubuntu Trusty GCC-8 + os: linux + dist: trusty + sudo: required env: - ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 " + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis + os: linux + dist: trusty + sudo: required + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" + - BUILD_OPENMW_CS="OFF" + compiler: clang + - name: OpenMW (openmw-cs) on Ubuntu Trusty Clang-7 with Static Analysis + os: linux + dist: trusty + sudo: required + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" + - BUILD_OPENMW="OFF" compiler: clang - allow_failures: - - env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 " +# allow_failures: +# - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis -before_install: ./CI/before_install.${TRAVIS_OS_NAME}.sh -before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh +before_install: + - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi + - ./CI/before_install.${TRAVIS_OS_NAME}.sh +before_script: + - ./CI/before_script.${TRAVIS_OS_NAME}.sh script: - cd ./build - - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi + - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE} make -j3; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index aecf2fa4d..0d686b668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,13 @@ ------ Bug #3623: Fix HiDPI on Windows + Bug #4540: Rain delay when exiting water + Bug #4701: PrisonMarker record is not hardcoded like other markers Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file + Feature #4673: Weapon sheathing Task #4686: Upgrade media decoder to a more current FFmpeg API - 0.45.0 ------ diff --git a/CI/before_install.linux.sh b/CI/before_install.linux.sh index 1ec88fb9e..fd4e4829c 100755 --- a/CI/before_install.linux.sh +++ b/CI/before_install.linux.sh @@ -1,3 +1,4 @@ -#!/bin/sh -e -sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang -sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++ +#!/bin/bash -ex + +sudo ln -sf /usr/bin/clang-7 /usr/local/bin/clang +sudo ln -sf /usr/bin/clang++-7 /usr/local/bin/clang++ diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index dd879989a..c8c4f8f85 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -ex free -m @@ -8,8 +8,22 @@ GOOGLETEST_DIR="$(pwd)/googletest/build" mkdir build cd build export CODE_COVERAGE=1 -if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi -${ANALYZE}cmake \ + +if [[ "${CC}" =~ "clang" ]]; then export CODE_COVERAGE=0; fi +if [[ -z "${BUILD_OPENMW}" ]]; then export BUILD_OPENMW=ON; fi +if [[ -z "${BUILD_OPENMW_CS}" ]]; then export BUILD_OPENMW_CS=ON; fi + +${ANALYZE} cmake \ + -DBUILD_OPENMW=${BUILD_OPENMW} \ + -DBUILD_OPENCS=${BUILD_OPENMW_CS} \ + -DBUILD_LAUNCHER=${BUILD_OPENMW_CS} \ + -DBUILD_BSATOOL=${BUILD_OPENMW_CS} \ + -DBUILD_ESMTOOL=${BUILD_OPENMW_CS} \ + -DBUILD_MWINIIMPORTER=${BUILD_OPENMW_CS} \ + -DBUILD_ESSIMPORTER=${BUILD_OPENMW_CS} \ + -DBUILD_WIZARD=${BUILD_OPENMW_CS} \ + -DBUILD_NIFTEST=${BUILD_OPENMW_CS} \ + -DBUILD_MYGUI_PLUGIN=${BUILD_OPENMW_CS} \ -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \ -DBUILD_UNITTESTS=1 \ -DCMAKE_INSTALL_PREFIX=/usr \ diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index f3f897a29..63c7a52a8 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -977,15 +977,19 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base void CSMWorld::Data::loadFallbackEntries() { // Load default marker definitions, if game files do not have them for some reason - std::pair markers[] = { - std::make_pair("divinemarker", "marker_divine.nif"), - std::make_pair("doormarker", "marker_arrow.nif"), - std::make_pair("northmarker", "marker_north.nif"), - std::make_pair("templemarker", "marker_temple.nif"), - std::make_pair("travelmarker", "marker_travel.nif") + std::pair staticMarkers[] = { + std::make_pair("DivineMarker", "marker_divine.nif"), + std::make_pair("DoorMarker", "marker_arrow.nif"), + std::make_pair("NorthMarker", "marker_north.nif"), + std::make_pair("TempleMarker", "marker_temple.nif"), + std::make_pair("TravelMarker", "marker_travel.nif") }; - for (const std::pair marker : markers) + std::pair doorMarkers[] = { + std::make_pair("PrisonMarker", "marker_prison.nif") + }; + + for (const std::pair marker : staticMarkers) { if (mReferenceables.searchId (marker.first)==-1) { @@ -995,6 +999,17 @@ void CSMWorld::Data::loadFallbackEntries() mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static); } } + + for (const std::pair marker : doorMarkers) + { + if (mReferenceables.searchId (marker.first)==-1) + { + CSMWorld::Record record; + record.mBase = ESM::Door(marker.first, std::string(), marker.second, std::string(), std::string(), std::string()); + record.mState = CSMWorld::RecordBase::State_BaseOnly; + mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Door); + } + } } bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) diff --git a/apps/opencs/model/world/infoselectwrapper.cpp b/apps/opencs/model/world/infoselectwrapper.cpp index 3bc9bf4d2..3200d39fc 100644 --- a/apps/opencs/model/world/infoselectwrapper.cpp +++ b/apps/opencs/model/world/infoselectwrapper.cpp @@ -520,7 +520,6 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange const float FloatMax = std::numeric_limits::infinity(); const float FloatMin = -std::numeric_limits::infinity(); const float Epsilon = std::numeric_limits::epsilon(); - const std::pair InvalidRange(FloatMax, FloatMin); float value = mConstSelect.mValue.getFloat(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 8a6f7d23d..c89ea8b7f 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -166,7 +166,7 @@ namespace MWClass getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); if (hasInventory) - getInventoryStore(ptr).autoEquipShield(ptr); + getInventoryStore(ptr).autoEquip(ptr); } } diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index e6aff8d19..e4cf28394 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -1,5 +1,4 @@ #include "actoranimation.hpp" - #include #include @@ -9,11 +8,22 @@ #include #include +#include +#include + +#include #include #include +#include #include +#include + +#include + +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" @@ -43,6 +53,8 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr // Make sure we cleaned object from effects, just in cast if we re-use node removeEffects(); + + mWeaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game"); } ActorAnimation::~ActorAnimation() @@ -51,6 +63,302 @@ ActorAnimation::~ActorAnimation() { mInsert->removeChild(iter->second); } + + mScabbard.reset(); +} + +PartHolderPtr ActorAnimation::getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor) +{ + osg::Group* parent = getBoneByName(bonename); + if (!parent) + return nullptr; + + osg::ref_ptr instance = mResourceSystem->getSceneManager()->getInstance(model, parent); + + const NodeMap& nodeMap = getNodeMap(); + NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename)); + if (found == nodeMap.end()) + return PartHolderPtr(); + + if (enchantedGlow) + addGlow(instance, *glowColor); + + return PartHolderPtr(new PartHolder(instance)); +} + +osg::Group* ActorAnimation::getBoneByName(std::string boneName) +{ + if (!mObjectRoot) + return nullptr; + + SceneUtil::FindByNameVisitor findVisitor (boneName); + mObjectRoot->accept(findVisitor); + + return findVisitor.mFoundNode; +} + +std::string ActorAnimation::getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon) +{ + std::string boneName; + if(weapon.isEmpty()) + return boneName; + + const std::string &type = weapon.getClass().getTypeName(); + if(type == typeid(ESM::Weapon).name()) + { + const MWWorld::LiveCellRef *ref = weapon.get(); + ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType; + return getHolsteredWeaponBoneName(weaponType); + } + + return boneName; +} + +std::string ActorAnimation::getHolsteredWeaponBoneName(const unsigned int weaponType) +{ + std::string boneName; + + switch(weaponType) + { + case ESM::Weapon::ShortBladeOneHand: + boneName = "Bip01 ShortBladeOneHand"; + break; + case ESM::Weapon::LongBladeOneHand: + boneName = "Bip01 LongBladeOneHand"; + break; + case ESM::Weapon::BluntOneHand: + boneName = "Bip01 BluntOneHand"; + break; + case ESM::Weapon::AxeOneHand: + boneName = "Bip01 LongBladeOneHand"; + break; + case ESM::Weapon::LongBladeTwoHand: + boneName = "Bip01 LongBladeTwoClose"; + break; + case ESM::Weapon::BluntTwoClose: + boneName = "Bip01 BluntTwoClose"; + break; + case ESM::Weapon::AxeTwoHand: + boneName = "Bip01 AxeTwoClose"; + break; + case ESM::Weapon::BluntTwoWide: + boneName = "Bip01 BluntTwoWide"; + break; + case ESM::Weapon::SpearTwoWide: + boneName = "Bip01 SpearTwoWide"; + break; + case ESM::Weapon::MarksmanBow: + boneName = "Bip01 MarksmanBow"; + break; + case ESM::Weapon::MarksmanCrossbow: + boneName = "Bip01 MarksmanCrossbow"; + break; + case ESM::Weapon::MarksmanThrown: + boneName = "Bip01 MarksmanThrown"; + break; + default: + break; + } + + return boneName; +} + +void ActorAnimation::injectWeaponBones() +{ + if (!mResourceSystem->getVFS()->exists("meshes\\xbase_anim_sh.nif")) + { + mWeaponSheathing = false; + return; + } + + osg::ref_ptr sheathSkeleton = mResourceSystem->getSceneManager()->getInstance("meshes\\xbase_anim_sh.nif"); + + for (unsigned int type=0; type<=ESM::Weapon::MarksmanThrown; ++type) + { + const std::string holsteredBoneName = getHolsteredWeaponBoneName(type); + + SceneUtil::FindByNameVisitor findVisitor (holsteredBoneName); + sheathSkeleton->accept(findVisitor); + osg::ref_ptr sheathNode = findVisitor.mFoundNode; + + if (sheathNode && sheathNode.get()->getNumParents()) + { + osg::Group* sheathParent = getBoneByName(sheathNode.get()->getParent(0)->getName()); + + if (sheathParent) + { + sheathNode.get()->getParent(0)->removeChild(sheathNode); + sheathParent->addChild(sheathNode); + } + } + } +} + +// To make sure we do not run morph controllers for weapons, i.e. bows +class EmptyCallback : public osg::NodeCallback +{ + public: + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + } +}; + +void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) +{ + if (!mWeaponSheathing) + return; + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + mScabbard.reset(); + + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + // Since throwing weapons stack themselves, do not show such weapon itself + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + showHolsteredWeapons = false; + + std::string mesh = weapon->getClass().getModel(*weapon); + std::string scabbardName = mesh; + + std::string boneName = getHolsteredWeaponBoneName(*weapon); + if (mesh.empty() || boneName.empty()) + return; + + // If the scabbard is not found, use a weapon mesh as fallback + scabbardName = scabbardName.replace(scabbardName.size()-4, 4, "_sh.nif"); + bool isEnchanted = !weapon->getClass().getEnchantment(*weapon).empty(); + if(!mResourceSystem->getVFS()->exists(scabbardName)) + { + if (showHolsteredWeapons) + { + osg::Vec4f glowColor = getEnchantmentColor(*weapon); + mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor); + if (mScabbard) + mScabbard->getNode()->setUpdateCallback(new EmptyCallback); + } + + return; + } + + mScabbard = getWeaponPart(scabbardName, boneName); + + osg::Group* weaponNode = getBoneByName("Bip01 Weapon"); + if (!weaponNode) + return; + + // When we draw weapon, hide the Weapon node from sheath model. + // Otherwise add the enchanted glow to it. + if (!showHolsteredWeapons) + { + weaponNode->setNodeMask(0); + } + else + { + // If mesh author declared empty weapon node, use transformation from this node, but use the common weapon mesh. + // This approach allows to tweak weapon position without need to store the whole weapon mesh in the _sh file. + if (!weaponNode->getNumChildren()) + { + osg::ref_ptr fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, weaponNode); + fallbackNode->setUpdateCallback(new EmptyCallback); + } + + if (isEnchanted) + { + osg::Vec4f glowColor = getEnchantmentColor(*weapon); + addGlow(weaponNode, glowColor); + } + } +} + +void ActorAnimation::updateQuiver() +{ + if (!mWeaponSheathing) + return; + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + std::string mesh = weapon->getClass().getModel(*weapon); + std::string boneName = getHolsteredWeaponBoneName(*weapon); + if (mesh.empty() || boneName.empty()) + return; + + osg::Group* ammoNode = getBoneByName("Bip01 Ammo"); + if (!ammoNode) + return; + + // Special case for throwing weapons - they do not use ammo, but they stack themselves + bool suitableAmmo = false; + MWWorld::ConstContainerStoreIterator ammo = weapon; + unsigned int ammoCount = 0; + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + { + ammoCount = ammo->getRefData().getCount(); + osg::Group* throwingWeaponNode = getBoneByName("Weapon Bone"); + if (throwingWeaponNode && throwingWeaponNode->getNumChildren()) + ammoCount--; + + suitableAmmo = true; + } + else + { + ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + + ammoCount = ammo->getRefData().getCount(); + bool arrowAttached = isArrowAttached(); + if (arrowAttached) + ammoCount--; + + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + suitableAmmo = ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt; + else if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow) + suitableAmmo = ammo->get()->mBase->mData.mType == ESM::Weapon::Arrow; + } + + if (ammoNode && suitableAmmo) + { + // We should not show more ammo than equipped and more than quiver mesh has + ammoCount = std::min(ammoCount, ammoNode->getNumChildren()); + + // Remove existing ammo nodes + for (unsigned int i=0; igetNumChildren(); ++i) + { + osg::ref_ptr arrowNode = ammoNode->getChild(i)->asGroup(); + if (!arrowNode->getNumChildren()) + continue; + + osg::ref_ptr arrowChildNode = arrowNode->getChild(0); + arrowNode->removeChild(arrowChildNode); + } + + // Add new ones + osg::Vec4f glowColor = getEnchantmentColor(*ammo); + std::string model = ammo->getClass().getModel(*ammo); + for (unsigned int i=0; i arrowNode = ammoNode->getChild(i)->asGroup(); + osg::ref_ptr arrow = mResourceSystem->getSceneManager()->getInstance(model, arrowNode); + if (!ammo->getClass().getEnchantment(*ammo).empty()) + addGlow(arrow, glowColor); + } + } + + // recreate shaders for invisible actors, otherwise new nodes will be visible + if (mAlpha != 1.f) + mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/) @@ -63,6 +371,24 @@ void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/) addHiddenItemLight(item, light); } } + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + // If the count of equipped ammo or throwing weapon was changed, we should update quiver + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + MWWorld::ConstContainerStoreIterator ammo = inv.end(); + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + ammo = weapon; + else + ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + + if(ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId()) + updateQuiver(); } void ActorAnimation::itemRemoved(const MWWorld::ConstPtr& item, int /*count*/) @@ -78,6 +404,24 @@ void ActorAnimation::itemRemoved(const MWWorld::ConstPtr& item, int /*count*/) } } } + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + // If the count of equipped ammo or throwing weapon was changed, we should update quiver + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + MWWorld::ConstContainerStoreIterator ammo = inv.end(); + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + ammo = weapon; + else + ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + + if(ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId()) + updateQuiver(); } void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight) diff --git a/apps/openmw/mwrender/actoranimation.hpp b/apps/openmw/mwrender/actoranimation.hpp index c00f05718..7a8acd3a8 100644 --- a/apps/openmw/mwrender/actoranimation.hpp +++ b/apps/openmw/mwrender/actoranimation.hpp @@ -6,6 +6,7 @@ #include #include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "animation.hpp" @@ -36,6 +37,24 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener virtual void itemAdded(const MWWorld::ConstPtr& item, int count); virtual void itemRemoved(const MWWorld::ConstPtr& item, int count); + virtual bool isArrowAttached() const { return false; } + + protected: + bool mWeaponSheathing; + osg::Group* getBoneByName(std::string boneName); + virtual void updateHolsteredWeapon(bool showHolsteredWeapons); + virtual void injectWeaponBones(); + virtual void updateQuiver(); + virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon); + virtual std::string getHolsteredWeaponBoneName(const unsigned int weaponType); + virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor); + virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename) + { + osg::Vec4f stubColor = osg::Vec4f(0,0,0,0); + return getWeaponPart(model, bonename, false, &stubColor); + }; + + PartHolderPtr mScabbard; private: void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index bf293bd55..7730b9104 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1748,8 +1748,6 @@ namespace MWRender } else { - osg::StateSet* stateset (new osg::StateSet); - osg::BlendFunc* blendfunc (new osg::BlendFunc); stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index a262d0021..aeee69ab0 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -49,7 +49,12 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const setObjectRoot(model, true, false, true); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) + { + if (mWeaponSheathing) + injectWeaponBones(); + addAnimSource("meshes\\xbase_anim.nif", model); + } addAnimSource(model, model); mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr); @@ -84,6 +89,9 @@ void CreatureWeaponAnimation::updateParts() mWeapon.reset(); mShield.reset(); + updateHolsteredWeapon(!mShowWeapons); + updateQuiver(); + if (mShowWeapons) updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight); if (mShowCarriedLeft) @@ -157,14 +165,21 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) } } +bool CreatureWeaponAnimation::isArrowAttached() const +{ + return mAmmunition != nullptr; +} + void CreatureWeaponAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); + updateQuiver(); } void CreatureWeaponAnimation::releaseArrow(float attackStrength) { WeaponAnimation::releaseArrow(mPtr, attackStrength); + updateQuiver(); } osg::Group *CreatureWeaponAnimation::getArrowBone() diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index ff959a551..d0fd5bdb4 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -54,6 +54,8 @@ namespace MWRender /// to indicate the facing orientation of the character. virtual void setPitchFactor(float factor) { mPitchFactor = factor; } + protected: + virtual bool isArrowAttached() const; private: PartHolderPtr mWeapon; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c7669f5e9..f21f24884 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include // TextKeyMapHolder #include "../mwworld/esmstore.hpp" @@ -308,6 +310,12 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) if(mViewMode == viewMode) return; + // Disable weapon sheathing in the 1st-person mode + if (viewMode == VM_FirstPerson) + mWeaponSheathing = false; + else + mWeaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game"); + mViewMode = viewMode; rebuild(); @@ -389,6 +397,7 @@ void NpcAnimation::setRenderBin() void NpcAnimation::rebuild() { + mScabbard.reset(); updateNpcBase(); MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); @@ -460,6 +469,11 @@ void NpcAnimation::updateNpcBase() setObjectRoot(smodel, true, true, false); + if (mWeaponSheathing) + injectWeaponBones(); + + updateParts(); + if(!is1stPerson) { const std::string base = "meshes\\xbase_anim.nif"; @@ -488,8 +502,6 @@ void NpcAnimation::updateNpcBase() mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView)); } - updateParts(); - mWeaponAnimationTime->updateStartTime(); } @@ -899,7 +911,8 @@ void NpcAnimation::showWeapons(bool showWeapon) attachArrow(); } } - if (mAlpha != 1.f) + // Note: we will need to recreate shaders later if we use weapon sheathing anyway, so there is no point to update them here + if (mAlpha != 1.f && !mWeaponSheathing) mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } else @@ -909,6 +922,9 @@ void NpcAnimation::showWeapons(bool showWeapon) if (mPtr == MWMechanics::getPlayer()) MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); } + + updateHolsteredWeapon(!mShowWeapons); + updateQuiver(); } void NpcAnimation::showCarriedLeft(bool show) @@ -936,11 +952,13 @@ void NpcAnimation::showCarriedLeft(bool show) void NpcAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); + updateQuiver(); } void NpcAnimation::releaseArrow(float attackStrength) { WeaponAnimation::releaseArrow(mPtr, attackStrength); + updateQuiver(); } osg::Group* NpcAnimation::getArrowBone() @@ -1185,4 +1203,9 @@ void NpcAnimation::setAccurateAiming(bool enabled) mAccurateAiming = enabled; } +bool NpcAnimation::isArrowAttached() const +{ + return mAmmunition != nullptr; +} + } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 335ca5d5a..1fbdd863c 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -95,6 +95,7 @@ private: protected: virtual void addControllers(); + virtual bool isArrowAttached() const; public: /** diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index d6a0b9f01..0ea0905a6 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -401,11 +401,15 @@ public: { } - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + bool isUnderwater() { osg::Vec3f eyePoint = mCameraRelativeTransform->getLastEyePoint(); + return mEnabled && eyePoint.z() < mWaterLevel; + } - if (mEnabled && eyePoint.z() < mWaterLevel) + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (isUnderwater()) return; traverse(node, nv); @@ -1575,6 +1579,8 @@ void SkyManager::update(float duration) mRainIntensityUniform->set((float) mWeatherAlpha); } + switchUnderwaterRain(); + if (mIsStorm) { osg::Quat quat; @@ -1626,6 +1632,15 @@ void SkyManager::updateRainParameters() } } +void SkyManager::switchUnderwaterRain() +{ + if (!mRainParticleSystem) + return; + + bool freeze = mUnderwaterSwitch->isUnderwater(); + mRainParticleSystem->setFrozen(freeze); +} + void SkyManager::setWeather(const WeatherResult& weather) { if (!mCreated) return; diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index a9345cdb4..e70f16521 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -180,6 +180,7 @@ namespace MWRender void createRain(); void destroyRain(); + void switchUnderwaterRain(); void updateRainParameters(); Resource::SceneManager* mSceneManager; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 6a24ae4f0..ee3089901 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -330,7 +330,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1); } - if (mListener) + // we should not fire event for InventoryStore yet - it has some custom logic + if (mListener && !actorPtr.getClass().hasInventoryStore(actorPtr)) mListener->itemAdded(item, count); return it; @@ -439,7 +440,8 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor flagAsModified(); - if (mListener) + // we should not fire event for InventoryStore yet - it has some custom logic + if (mListener && !actor.getClass().hasInventoryStore(actor)) mListener->itemRemoved(item, count - toRemove); // number of removed items diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b67eb6552..4564d2fa3 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -68,6 +68,9 @@ namespace MWWorld static const std::string sGoldId; + protected: + ContainerStoreListener* mListener; + private: MWWorld::CellRefList potions; @@ -87,8 +90,6 @@ namespace MWWorld ///< Stores result of levelled item spawns. <(refId, spawningGroup), count> /// This is used to restock levelled items(s) if the old item was sold. - ContainerStoreListener* mListener; - mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 01d8e4b82..c85e3d30e 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -141,6 +141,7 @@ void ESMStore::setUp(bool validateRecords) mAttributes.setUp(); mDialogs.setUp(); mStatics.setUp(); + mDoors.setUp(); if (validateRecords) validate(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 647ae261b..6fe1d0c73 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -99,7 +99,8 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt } MWWorld::InventoryStore::InventoryStore() - : mListener(nullptr) + : ContainerStore() + , mInventoryListener(nullptr) , mUpdatesEnabled (true) , mFirstAutoEquip(true) , mSelectedEnchantItem(end()) @@ -111,7 +112,7 @@ MWWorld::InventoryStore::InventoryStore() MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) : ContainerStore (store) , mMagicEffects(store.mMagicEffects) - , mListener(store.mListener) + , mInventoryListener(store.mInventoryListener) , mUpdatesEnabled(store.mUpdatesEnabled) , mFirstAutoEquip(store.mFirstAutoEquip) , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes) @@ -124,6 +125,7 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store) { mListener = store.mListener; + mInventoryListener = store.mInventoryListener; mMagicEffects = store.mMagicEffects; mFirstAutoEquip = store.mFirstAutoEquip; mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; @@ -147,6 +149,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, autoEquip(actorPtr); } + if (mListener) + mListener->itemAdded(itemPtr, count); + return retVal; } @@ -246,25 +251,195 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) con return mSlots[slot]; } -void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) +void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots& slots_) +{ + if (!actor.getClass().isNpc()) + { + // In original game creatures do not autoequip weapon, but we need it for weapon sheathing. + // The only case when the difference is noticable - when this creature sells weapon. + // So just disable weapon autoequipping for creatures which sells weapon. + int services = actor.getClass().getServices(actor); + bool sellsWeapon = services & (ESM::NPC::Weapon|ESM::NPC::MagicItems); + if (sellsWeapon) + return; + } + + static const ESM::Skill::SkillEnum weaponSkills[] = + { + ESM::Skill::LongBlade, + ESM::Skill::Axe, + ESM::Skill::Spear, + ESM::Skill::ShortBlade, + ESM::Skill::Marksman, + ESM::Skill::BluntWeapon + }; + const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]); + + bool weaponSkillVisited[weaponSkillsLength] = { false }; + + // give arrows/bolt with max damage by default + int arrowMax = 0; + int boltMax = 0; + ContainerStoreIterator arrow(end()); + ContainerStoreIterator bolt(end()); + + // rate ammo + for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter) + { + const ESM::Weapon* esmWeapon = iter->get()->mBase; + + if (esmWeapon->mData.mType == ESM::Weapon::Arrow) + { + if (esmWeapon->mData.mChop[1] >= arrowMax) + { + arrowMax = esmWeapon->mData.mChop[1]; + arrow = iter; + } + } + else if (esmWeapon->mData.mType == ESM::Weapon::Bolt) + { + if (esmWeapon->mData.mChop[1] >= boltMax) + { + boltMax = esmWeapon->mData.mChop[1]; + bolt = iter; + } + } + } + + // rate weapon + for (int i = 0; i < static_cast(weaponSkillsLength); ++i) + { + int max = 0; + int maxWeaponSkill = -1; + + for (int j = 0; j < static_cast(weaponSkillsLength); ++j) + { + int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); + if (skillValue > max && !weaponSkillVisited[j]) + { + max = skillValue; + maxWeaponSkill = j; + } + } + + if (maxWeaponSkill == -1) + break; + + max = 0; + ContainerStoreIterator weapon(end()); + + for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter) + { + if (!canActorAutoEquip(actor, *iter)) + continue; + + const ESM::Weapon* esmWeapon = iter->get()->mBase; + + if (esmWeapon->mData.mType == ESM::Weapon::Arrow || esmWeapon->mData.mType == ESM::Weapon::Bolt) + continue; + + if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill]) + { + if (esmWeapon->mData.mChop[1] >= max) + { + max = esmWeapon->mData.mChop[1]; + weapon = iter; + } + + if (esmWeapon->mData.mSlash[1] >= max) + { + max = esmWeapon->mData.mSlash[1]; + weapon = iter; + } + + if (esmWeapon->mData.mThrust[1] >= max) + { + max = esmWeapon->mData.mThrust[1]; + weapon = iter; + } + } + } + + bool isBow = false; + bool isCrossbow = false; + if (weapon != end()) + { + const MWWorld::LiveCellRef *ref = weapon->get(); + ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; + + if (type == ESM::Weapon::MarksmanBow) + isBow = true; + else if (type == ESM::Weapon::MarksmanCrossbow) + isCrossbow = true; + } + + if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first) + { + // Do not equip ranged weapons, if there is no suitable ammo + bool hasAmmo = true; + if (isBow == true) + { + if (arrow == end()) + hasAmmo = false; + else + slots_[Slot_Ammunition] = arrow; + } + if (isCrossbow == true) + { + if (bolt == end()) + hasAmmo = false; + else + slots_[Slot_Ammunition] = bolt; + } + + if (hasAmmo) + { + std::pair, bool> itemsSlots = weapon->getClass().getEquipmentSlots (*weapon); + + if (!itemsSlots.first.empty()) + { + if (!itemsSlots.second) + { + if (weapon->getRefData().getCount() > 1) + { + unstack(*weapon, actor); + } + } + + int slot = itemsSlots.first.front(); + slots_[slot] = weapon; + + if (!isBow && !isCrossbow) + slots_[Slot_Ammunition] = end(); + } + + break; + } + } + + weaponSkillVisited[maxWeaponSkill] = true; + } +} + +void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots& slots_) { + // Only NPCs can wear armor for now. + // For creatures we equip only shields. + if (!actor.getClass().isNpc()) + { + autoEquipShield(actor, slots_); + return; + } + const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); + int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); - TSlots slots_; - initSlots (slots_); - - // Disable model update during auto-equip - mUpdatesEnabled = false; - - // Autoequip clothing, armor and weapons. - // Equipping lights is handled in Actors::updateEquippedLight based on environment light. for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) { Ptr test = *iter; @@ -289,12 +464,12 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) std::pair, bool> itemsSlots = iter->getClass().getEquipmentSlots (*iter); - // checking if current item poited by iter can be equipped + // checking if current item pointed by iter can be equipped for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { // if true then it means slot is equipped already - // check if slot may require swapping if current item is more valueable + // check if slot may require swapping if current item is more valuable if (slots_.at (*iter2)!=end()) { Ptr old = *slots_.at (*iter2); @@ -362,161 +537,67 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) break; } } - - static const ESM::Skill::SkillEnum weaponSkills[] = - { - ESM::Skill::LongBlade, - ESM::Skill::Axe, - ESM::Skill::Spear, - ESM::Skill::ShortBlade, - ESM::Skill::Marksman, - ESM::Skill::BluntWeapon - }; - const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]); - - bool weaponSkillVisited[weaponSkillsLength] = { false }; - - for (int i = 0; i < static_cast(weaponSkillsLength); ++i) - { - int max = 0; - int maxWeaponSkill = -1; - - for (int j = 0; j < static_cast(weaponSkillsLength); ++j) - { - int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); - - if (skillValue > max && !weaponSkillVisited[j]) - { - max = skillValue; - maxWeaponSkill = j; - } - } - - if (maxWeaponSkill == -1) - break; - - max = 0; - ContainerStoreIterator weapon(end()); - - for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter) - { - if (!canActorAutoEquip(actor, *iter)) - continue; - - const ESM::Weapon* esmWeapon = iter->get()->mBase; - - if (esmWeapon->mData.mType == ESM::Weapon::Arrow || esmWeapon->mData.mType == ESM::Weapon::Bolt) - continue; - - if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill]) - { - if (esmWeapon->mData.mChop[1] >= max) - { - max = esmWeapon->mData.mChop[1]; - weapon = iter; - } - - if (esmWeapon->mData.mSlash[1] >= max) - { - max = esmWeapon->mData.mSlash[1]; - weapon = iter; - } - - if (esmWeapon->mData.mThrust[1] >= max) - { - max = esmWeapon->mData.mThrust[1]; - weapon = iter; - } - } - } - - if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first) - { - std::pair, bool> itemsSlots = - weapon->getClass().getEquipmentSlots (*weapon); - - if (!itemsSlots.first.empty()) - { - if (!itemsSlots.second) - { - if (weapon->getRefData().getCount() > 1) - { - unstack(*weapon, actor); - } - } - - int slot = itemsSlots.first.front(); - slots_[slot] = weapon; - } - - break; - } - - weaponSkillVisited[maxWeaponSkill] = true; - } - - bool changed = false; - - for (std::size_t i=0; iget()->mBase->mData.mType != ESM::Armor::Shield) continue; - if (iter->getClass().canBeEquipped(*iter, actor).first != 1) continue; - if (iter->getClass().getItemHealth(*iter) <= 0) continue; - std::pair, bool> shieldSlots = iter->getClass().getEquipmentSlots(*iter); - if (shieldSlots.first.empty()) continue; - int slot = shieldSlots.first[0]; const ContainerStoreIterator& shield = mSlots[slot]; - if (shield != end() && shield.getType() == Type_Armor && shield->get()->mBase->mData.mType == ESM::Armor::Shield) { if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter)) continue; } + slots_[slot] = iter; + } +} - equip(slot, iter, actor); - updated = true; +void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) +{ + TSlots slots_; + initSlots (slots_); + + // Disable model update during auto-equip + mUpdatesEnabled = false; + + // Autoequip clothing, armor and weapons. + // Equipping lights is handled in Actors::updateEquippedLight based on environment light. + // Note: creatures do not use the armor mitigation and can equip only shields + // Use a custom logic for them - select shield based on its health instead of armor rating (since it useless for creatures) + autoEquipWeapon(actor, slots_); + autoEquipArmor(actor, slots_); + + bool changed = false; + + for (std::size_t i=0; ipermanentEffectAdded(magicEffect, !mFirstAutoEquip); + mInventoryListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip); } if (magnitude) @@ -737,6 +818,9 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor mSelectedEnchantItem = end(); } + if (mListener) + mListener->itemRemoved(item, retCount); + return retCount; } @@ -822,12 +906,12 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(con MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getInvListener() { - return mListener; + return mInventoryListener; } void MWWorld::InventoryStore::setInvListener(InventoryStoreListener *listener, const Ptr& actor) { - mListener = listener; + mInventoryListener = listener; updateMagicEffects(actor); } @@ -835,8 +919,8 @@ void MWWorld::InventoryStore::fireEquipmentChangedEvent(const Ptr& actor) { if (!mUpdatesEnabled) return; - if (mListener) - mListener->equipmentChanged(); + if (mInventoryListener) + mInventoryListener->equipmentChanged(); // if player, update inventory window /* diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 49991c164..e99a99bfa 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -69,7 +69,7 @@ namespace MWWorld MWMechanics::MagicEffects mMagicEffects; - InventoryStoreListener* mListener; + InventoryStoreListener* mInventoryListener; // Enables updates of magic effects and actor model whenever items are equipped or unequipped. // This is disabled during autoequip to avoid excessive updates @@ -94,6 +94,10 @@ namespace MWWorld TSlots mSlots; + void autoEquipWeapon(const MWWorld::Ptr& actor, TSlots& slots_); + void autoEquipArmor(const MWWorld::Ptr& actor, TSlots& slots_); + void autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_); + // selected magic item (for using enchantments of type "Cast once" or "Cast when used") ContainerStoreIterator mSelectedEnchantItem; @@ -164,9 +168,6 @@ namespace MWWorld void autoEquip (const MWWorld::Ptr& actor); ///< Auto equip items according to stats and item value. - void autoEquipShield(const MWWorld::Ptr& actor); - ///< Auto-equip the shield with most health. - const MWMechanics::MagicEffects& getMagicEffects() const; ///< Return magic effects from worn items. diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index b6bf2b7eb..ec9965a6c 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1059,11 +1059,11 @@ namespace MWWorld { // Load default marker definitions, if game files do not have them for some reason std::pair markers[] = { - std::make_pair("divinemarker", "marker_divine.nif"), - std::make_pair("doormarker", "marker_arrow.nif"), - std::make_pair("northmarker", "marker_north.nif"), - std::make_pair("templemarker", "marker_temple.nif"), - std::make_pair("travelmarker", "marker_travel.nif") + std::make_pair("DivineMarker", "marker_divine.nif"), + std::make_pair("DoorMarker", "marker_arrow.nif"), + std::make_pair("NorthMarker", "marker_north.nif"), + std::make_pair("TempleMarker", "marker_temple.nif"), + std::make_pair("TravelMarker", "marker_travel.nif") }; for (const std::pair marker : markers) @@ -1080,6 +1080,28 @@ namespace MWWorld } } + template<> + void Store::setUp() + { + // Load default Door type marker definitions + std::pair markers[] = { + std::make_pair("PrisonMarker", "marker_prison.nif") + }; + + for (const std::pair marker : markers) + { + if (search(marker.first) == 0) + { + ESM::Door newMarker = ESM::Door(marker.first, std::string(), marker.second, std::string(), std::string(), std::string()); + std::pair ret = mStatic.insert(std::make_pair(marker.first, newMarker)); + if (ret.first != mStatic.end()) + { + mShared.push_back(&ret.first->second); + } + } + } + } + template <> inline RecordId Store::load(ESM::ESMReader &esm) { // The original letter case of a dialogue ID is saved, because it's printed diff --git a/apps/wizard/existinginstallationpage.cpp b/apps/wizard/existinginstallationpage.cpp index 3ec98a935..4c05f5500 100644 --- a/apps/wizard/existinginstallationpage.cpp +++ b/apps/wizard/existinginstallationpage.cpp @@ -31,11 +31,13 @@ void Wizard::ExistingInstallationPage::initializePage() // Hide the default item if there are installations to choose from installationsList->item(0)->setHidden(!paths.isEmpty()); - foreach (const QString &path, paths) { - QListWidgetItem *item = new QListWidgetItem(path); - + foreach (const QString &path, paths) + { if (installationsList->findItems(path, Qt::MatchExactly).isEmpty()) + { + QListWidgetItem *item = new QListWidgetItem(path); installationsList->addItem(item); + } } connect(installationsList, SIGNAL(currentTextChanged(QString)), diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 41407ec88..39e91d5a3 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -168,7 +168,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int case Qt::DisplayRole: { if (column >=0 && column <=EsmFile::FileProperty_GameFile) - return file->fileProperty(static_cast(column)); + return file->fileProperty(static_cast(column)); return QVariant(); } diff --git a/components/detournavigator/recastallocutils.hpp b/components/detournavigator/recastallocutils.hpp index 7b083d139..69a5cfa8b 100644 --- a/components/detournavigator/recastallocutils.hpp +++ b/components/detournavigator/recastallocutils.hpp @@ -86,17 +86,6 @@ namespace DetourNavigator return static_cast(ptr) + 1; } - // TODO: use std::align - inline void* align(std::size_t align, std::size_t size, void*& ptr, std::size_t& space) noexcept - { - const auto intptr = reinterpret_cast(ptr); - const auto aligned = (intptr - 1u + align) & - align; - const auto diff = aligned - intptr; - if ((size + diff) > space) - return nullptr; - space -= diff; - return ptr = reinterpret_cast(aligned); - } } #endif diff --git a/components/detournavigator/recasttempallocator.hpp b/components/detournavigator/recasttempallocator.hpp index e369b4224..fbf9fd62c 100644 --- a/components/detournavigator/recasttempallocator.hpp +++ b/components/detournavigator/recasttempallocator.hpp @@ -21,7 +21,7 @@ namespace DetourNavigator std::size_t space = mStack.size() - getUsedSize(); void* top = mTop; const auto itemSize = 2 * sizeof(std::size_t) + size; - if (rcUnlikely(!align(sizeof(std::size_t), itemSize, top, space))) + if (rcUnlikely(!std::align(sizeof(std::size_t), itemSize, top, space))) return nullptr; setTempPtrBufferType(top, BufferType_temp); setTempPtrPrev(top, mPrev); diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index 3afe5d5e4..955abec89 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -22,6 +22,21 @@ struct Door void blank(); ///< Set record to default state (does not touch the ID). + + Door(const std::string id, const std::string name, const std::string &model, + const std::string script, const std::string opensound, const std::string closesound) + : mId(id) + , mName(name) + , mModel(model) + , mScript(script) + , mOpenSound(opensound) + , mCloseSound(closesound) + { + } + + Door() + { + } }; } #endif diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 845d0c484..4781ef3fc 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -134,9 +134,9 @@ public: { state->bindVertexBufferObject(bufferobject); - glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)nullptr + 12); - glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr + 16); + glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast(0)); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), static_cast(0) + 12); + glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast(0) + 16); } else { diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 8edb3d765..328a10cd1 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -396,7 +396,8 @@ namespace Resource { const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm", "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle", - "Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "ArrowBone", "Camera"}; + "Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"}; + reservedNames = std::vector(reserved, reserved + sizeof(reserved)/sizeof(reserved[0])); for (unsigned int i=0; i