mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 22:23:51 +00:00
Merge branch 'master' into pr/fix-hidpi-resolution
This commit is contained in:
commit
02a8c6a7ce
35 changed files with 870 additions and 269 deletions
54
.travis.yml
54
.travis.yml
|
@ -1,10 +1,4 @@
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
osx_image: xcode9.4
|
|
||||||
language: cpp
|
language: cpp
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
@ -20,16 +14,16 @@ addons:
|
||||||
sources:
|
sources:
|
||||||
- sourceline: 'ppa:openmw/openmw'
|
- sourceline: 'ppa:openmw/openmw'
|
||||||
- ubuntu-toolchain-r-test
|
- ubuntu-toolchain-r-test
|
||||||
- llvm-toolchain-precise-3.6
|
- llvm-toolchain-trusty-7
|
||||||
packages: [
|
packages: [
|
||||||
# Dev
|
# Dev
|
||||||
cmake, clang-3.6, libunshield-dev, libtinyxml-dev,
|
cmake, clang-7, clang-tools-7, gcc-8, g++-8,
|
||||||
# Boost
|
# Boost
|
||||||
libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev,
|
libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev,
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev,
|
libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev,
|
||||||
# Audio & Video
|
# Audio, Video and Misc. deps
|
||||||
libsdl2-dev, libqt4-dev, libopenal-dev,
|
libsdl2-dev, libqt4-dev, libopenal-dev, libunshield-dev, libtinyxml-dev,
|
||||||
# The other ones from OpenMW ppa
|
# The other ones from OpenMW ppa
|
||||||
libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev
|
libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev
|
||||||
]
|
]
|
||||||
|
@ -44,18 +38,44 @@ addons:
|
||||||
branch_pattern: coverity_scan
|
branch_pattern: coverity_scan
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
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:
|
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
|
compiler: clang
|
||||||
allow_failures:
|
- name: OpenMW (openmw-cs) on Ubuntu Trusty Clang-7 with Static Analysis
|
||||||
- env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 "
|
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:
|
||||||
|
# - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis
|
||||||
|
|
||||||
before_install: ./CI/before_install.${TRAVIS_OS_NAME}.sh
|
before_install:
|
||||||
before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
- 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:
|
script:
|
||||||
- cd ./build
|
- 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}" = "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 ./openmw_test_suite; fi
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
------
|
------
|
||||||
|
|
||||||
Bug #3623: Fix HiDPI on Windows
|
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 #2229: Improve pathfinding AI
|
||||||
Feature #3442: Default values for fallbacks from ini file
|
Feature #3442: Default values for fallbacks from ini file
|
||||||
|
Feature #4673: Weapon sheathing
|
||||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||||
|
|
||||||
|
|
||||||
0.45.0
|
0.45.0
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
#!/bin/sh -e
|
#!/bin/bash -ex
|
||||||
sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang
|
|
||||||
sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++
|
sudo ln -sf /usr/bin/clang-7 /usr/local/bin/clang
|
||||||
|
sudo ln -sf /usr/bin/clang++-7 /usr/local/bin/clang++
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh -e
|
#!/bin/bash -ex
|
||||||
|
|
||||||
free -m
|
free -m
|
||||||
|
|
||||||
|
@ -8,8 +8,22 @@ GOOGLETEST_DIR="$(pwd)/googletest/build"
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
export CODE_COVERAGE=1
|
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_WITH_CODE_COVERAGE=${CODE_COVERAGE} \
|
||||||
-DBUILD_UNITTESTS=1 \
|
-DBUILD_UNITTESTS=1 \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
|
|
@ -977,15 +977,19 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
|
||||||
void CSMWorld::Data::loadFallbackEntries()
|
void CSMWorld::Data::loadFallbackEntries()
|
||||||
{
|
{
|
||||||
// Load default marker definitions, if game files do not have them for some reason
|
// Load default marker definitions, if game files do not have them for some reason
|
||||||
std::pair<std::string, std::string> markers[] = {
|
std::pair<std::string, std::string> staticMarkers[] = {
|
||||||
std::make_pair("divinemarker", "marker_divine.nif"),
|
std::make_pair("DivineMarker", "marker_divine.nif"),
|
||||||
std::make_pair("doormarker", "marker_arrow.nif"),
|
std::make_pair("DoorMarker", "marker_arrow.nif"),
|
||||||
std::make_pair("northmarker", "marker_north.nif"),
|
std::make_pair("NorthMarker", "marker_north.nif"),
|
||||||
std::make_pair("templemarker", "marker_temple.nif"),
|
std::make_pair("TempleMarker", "marker_temple.nif"),
|
||||||
std::make_pair("travelmarker", "marker_travel.nif")
|
std::make_pair("TravelMarker", "marker_travel.nif")
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const std::pair<std::string, std::string> marker : markers)
|
std::pair<std::string, std::string> doorMarkers[] = {
|
||||||
|
std::make_pair("PrisonMarker", "marker_prison.nif")
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const std::pair<std::string, std::string> marker : staticMarkers)
|
||||||
{
|
{
|
||||||
if (mReferenceables.searchId (marker.first)==-1)
|
if (mReferenceables.searchId (marker.first)==-1)
|
||||||
{
|
{
|
||||||
|
@ -995,6 +999,17 @@ void CSMWorld::Data::loadFallbackEntries()
|
||||||
mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static);
|
mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const std::pair<std::string, std::string> marker : doorMarkers)
|
||||||
|
{
|
||||||
|
if (mReferenceables.searchId (marker.first)==-1)
|
||||||
|
{
|
||||||
|
CSMWorld::Record<ESM::Door> 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)
|
bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
|
||||||
|
|
|
@ -520,7 +520,6 @@ std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange
|
||||||
const float FloatMax = std::numeric_limits<float>::infinity();
|
const float FloatMax = std::numeric_limits<float>::infinity();
|
||||||
const float FloatMin = -std::numeric_limits<float>::infinity();
|
const float FloatMin = -std::numeric_limits<float>::infinity();
|
||||||
const float Epsilon = std::numeric_limits<float>::epsilon();
|
const float Epsilon = std::numeric_limits<float>::epsilon();
|
||||||
const std::pair<float, float> InvalidRange(FloatMax, FloatMin);
|
|
||||||
|
|
||||||
float value = mConstSelect.mValue.getFloat();
|
float value = mConstSelect.mValue.getFloat();
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,7 @@ namespace MWClass
|
||||||
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
|
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
|
||||||
|
|
||||||
if (hasInventory)
|
if (hasInventory)
|
||||||
getInventoryStore(ptr).autoEquipShield(ptr);
|
getInventoryStore(ptr).autoEquip(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "actoranimation.hpp"
|
#include "actoranimation.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
|
@ -9,11 +8,22 @@
|
||||||
#include <components/esm/loadligh.hpp>
|
#include <components/esm/loadligh.hpp>
|
||||||
#include <components/esm/loadcell.hpp>
|
#include <components/esm/loadcell.hpp>
|
||||||
|
|
||||||
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
|
||||||
|
#include <components/sceneutil/attach.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
#include <components/sceneutil/lightutil.hpp>
|
#include <components/sceneutil/lightutil.hpp>
|
||||||
|
#include <components/sceneutil/visitor.hpp>
|
||||||
|
|
||||||
#include <components/fallback/fallback.hpp>
|
#include <components/fallback/fallback.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
#include <components/vfs/manager.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
@ -43,6 +53,8 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group>
|
||||||
|
|
||||||
// Make sure we cleaned object from effects, just in cast if we re-use node
|
// Make sure we cleaned object from effects, just in cast if we re-use node
|
||||||
removeEffects();
|
removeEffects();
|
||||||
|
|
||||||
|
mWeaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game");
|
||||||
}
|
}
|
||||||
|
|
||||||
ActorAnimation::~ActorAnimation()
|
ActorAnimation::~ActorAnimation()
|
||||||
|
@ -51,6 +63,302 @@ ActorAnimation::~ActorAnimation()
|
||||||
{
|
{
|
||||||
mInsert->removeChild(iter->second);
|
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<osg::Node> 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<ESM::Weapon> *ref = weapon.get<ESM::Weapon>();
|
||||||
|
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<osg::Node> 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<osg::Node> 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<ESM::Weapon>()->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<osg::Node> 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<ESM::Weapon>()->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<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||||
|
suitableAmmo = ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt;
|
||||||
|
else if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanBow)
|
||||||
|
suitableAmmo = ammo->get<ESM::Weapon>()->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; i<ammoNode->getNumChildren(); ++i)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();
|
||||||
|
if (!arrowNode->getNumChildren())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> 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<ammoCount; ++i)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();
|
||||||
|
osg::ref_ptr<osg::Node> 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*/)
|
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);
|
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<ESM::Weapon>()->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*/)
|
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<ESM::Weapon>()->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)
|
void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include "../mwworld/containerstore.hpp"
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
#include "animation.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 itemAdded(const MWWorld::ConstPtr& item, int count);
|
||||||
virtual void itemRemoved(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:
|
private:
|
||||||
void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight);
|
void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight);
|
||||||
|
|
|
@ -1748,8 +1748,6 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
osg::StateSet* stateset (new osg::StateSet);
|
|
||||||
|
|
||||||
osg::BlendFunc* blendfunc (new osg::BlendFunc);
|
osg::BlendFunc* blendfunc (new osg::BlendFunc);
|
||||||
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,12 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const
|
||||||
setObjectRoot(model, true, false, true);
|
setObjectRoot(model, true, false, true);
|
||||||
|
|
||||||
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
|
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
|
||||||
|
{
|
||||||
|
if (mWeaponSheathing)
|
||||||
|
injectWeaponBones();
|
||||||
|
|
||||||
addAnimSource("meshes\\xbase_anim.nif", model);
|
addAnimSource("meshes\\xbase_anim.nif", model);
|
||||||
|
}
|
||||||
addAnimSource(model, model);
|
addAnimSource(model, model);
|
||||||
|
|
||||||
mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr);
|
mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr);
|
||||||
|
@ -84,6 +89,9 @@ void CreatureWeaponAnimation::updateParts()
|
||||||
mWeapon.reset();
|
mWeapon.reset();
|
||||||
mShield.reset();
|
mShield.reset();
|
||||||
|
|
||||||
|
updateHolsteredWeapon(!mShowWeapons);
|
||||||
|
updateQuiver();
|
||||||
|
|
||||||
if (mShowWeapons)
|
if (mShowWeapons)
|
||||||
updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);
|
updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
if (mShowCarriedLeft)
|
if (mShowCarriedLeft)
|
||||||
|
@ -157,14 +165,21 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreatureWeaponAnimation::isArrowAttached() const
|
||||||
|
{
|
||||||
|
return mAmmunition != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void CreatureWeaponAnimation::attachArrow()
|
void CreatureWeaponAnimation::attachArrow()
|
||||||
{
|
{
|
||||||
WeaponAnimation::attachArrow(mPtr);
|
WeaponAnimation::attachArrow(mPtr);
|
||||||
|
updateQuiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureWeaponAnimation::releaseArrow(float attackStrength)
|
void CreatureWeaponAnimation::releaseArrow(float attackStrength)
|
||||||
{
|
{
|
||||||
WeaponAnimation::releaseArrow(mPtr, attackStrength);
|
WeaponAnimation::releaseArrow(mPtr, attackStrength);
|
||||||
|
updateQuiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Group *CreatureWeaponAnimation::getArrowBone()
|
osg::Group *CreatureWeaponAnimation::getArrowBone()
|
||||||
|
|
|
@ -54,6 +54,8 @@ namespace MWRender
|
||||||
/// to indicate the facing orientation of the character.
|
/// to indicate the facing orientation of the character.
|
||||||
virtual void setPitchFactor(float factor) { mPitchFactor = factor; }
|
virtual void setPitchFactor(float factor) { mPitchFactor = factor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool isArrowAttached() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PartHolderPtr mWeapon;
|
PartHolderPtr mWeapon;
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include <components/sceneutil/skeleton.hpp>
|
#include <components/sceneutil/skeleton.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include <components/nifosg/nifloader.hpp> // TextKeyMapHolder
|
#include <components/nifosg/nifloader.hpp> // TextKeyMapHolder
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
@ -308,6 +310,12 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
||||||
if(mViewMode == viewMode)
|
if(mViewMode == viewMode)
|
||||||
return;
|
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;
|
mViewMode = viewMode;
|
||||||
rebuild();
|
rebuild();
|
||||||
|
|
||||||
|
@ -389,6 +397,7 @@ void NpcAnimation::setRenderBin()
|
||||||
|
|
||||||
void NpcAnimation::rebuild()
|
void NpcAnimation::rebuild()
|
||||||
{
|
{
|
||||||
|
mScabbard.reset();
|
||||||
updateNpcBase();
|
updateNpcBase();
|
||||||
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
|
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
|
||||||
|
@ -460,6 +469,11 @@ void NpcAnimation::updateNpcBase()
|
||||||
|
|
||||||
setObjectRoot(smodel, true, true, false);
|
setObjectRoot(smodel, true, true, false);
|
||||||
|
|
||||||
|
if (mWeaponSheathing)
|
||||||
|
injectWeaponBones();
|
||||||
|
|
||||||
|
updateParts();
|
||||||
|
|
||||||
if(!is1stPerson)
|
if(!is1stPerson)
|
||||||
{
|
{
|
||||||
const std::string base = "meshes\\xbase_anim.nif";
|
const std::string base = "meshes\\xbase_anim.nif";
|
||||||
|
@ -488,8 +502,6 @@ void NpcAnimation::updateNpcBase()
|
||||||
mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView));
|
mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParts();
|
|
||||||
|
|
||||||
mWeaponAnimationTime->updateStartTime();
|
mWeaponAnimationTime->updateStartTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -899,7 +911,8 @@ void NpcAnimation::showWeapons(bool showWeapon)
|
||||||
attachArrow();
|
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);
|
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -909,6 +922,9 @@ void NpcAnimation::showWeapons(bool showWeapon)
|
||||||
if (mPtr == MWMechanics::getPlayer())
|
if (mPtr == MWMechanics::getPlayer())
|
||||||
MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);
|
MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateHolsteredWeapon(!mShowWeapons);
|
||||||
|
updateQuiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpcAnimation::showCarriedLeft(bool show)
|
void NpcAnimation::showCarriedLeft(bool show)
|
||||||
|
@ -936,11 +952,13 @@ void NpcAnimation::showCarriedLeft(bool show)
|
||||||
void NpcAnimation::attachArrow()
|
void NpcAnimation::attachArrow()
|
||||||
{
|
{
|
||||||
WeaponAnimation::attachArrow(mPtr);
|
WeaponAnimation::attachArrow(mPtr);
|
||||||
|
updateQuiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpcAnimation::releaseArrow(float attackStrength)
|
void NpcAnimation::releaseArrow(float attackStrength)
|
||||||
{
|
{
|
||||||
WeaponAnimation::releaseArrow(mPtr, attackStrength);
|
WeaponAnimation::releaseArrow(mPtr, attackStrength);
|
||||||
|
updateQuiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Group* NpcAnimation::getArrowBone()
|
osg::Group* NpcAnimation::getArrowBone()
|
||||||
|
@ -1185,4 +1203,9 @@ void NpcAnimation::setAccurateAiming(bool enabled)
|
||||||
mAccurateAiming = enabled;
|
mAccurateAiming = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NpcAnimation::isArrowAttached() const
|
||||||
|
{
|
||||||
|
return mAmmunition != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,7 @@ private:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void addControllers();
|
virtual void addControllers();
|
||||||
|
virtual bool isArrowAttached() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -401,11 +401,15 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
bool isUnderwater()
|
||||||
{
|
{
|
||||||
osg::Vec3f eyePoint = mCameraRelativeTransform->getLastEyePoint();
|
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;
|
return;
|
||||||
|
|
||||||
traverse(node, nv);
|
traverse(node, nv);
|
||||||
|
@ -1575,6 +1579,8 @@ void SkyManager::update(float duration)
|
||||||
mRainIntensityUniform->set((float) mWeatherAlpha);
|
mRainIntensityUniform->set((float) mWeatherAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchUnderwaterRain();
|
||||||
|
|
||||||
if (mIsStorm)
|
if (mIsStorm)
|
||||||
{
|
{
|
||||||
osg::Quat quat;
|
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)
|
void SkyManager::setWeather(const WeatherResult& weather)
|
||||||
{
|
{
|
||||||
if (!mCreated) return;
|
if (!mCreated) return;
|
||||||
|
|
|
@ -180,6 +180,7 @@ namespace MWRender
|
||||||
|
|
||||||
void createRain();
|
void createRain();
|
||||||
void destroyRain();
|
void destroyRain();
|
||||||
|
void switchUnderwaterRain();
|
||||||
void updateRainParameters();
|
void updateRainParameters();
|
||||||
|
|
||||||
Resource::SceneManager* mSceneManager;
|
Resource::SceneManager* mSceneManager;
|
||||||
|
|
|
@ -330,7 +330,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
|
||||||
item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1);
|
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);
|
mListener->itemAdded(item, count);
|
||||||
|
|
||||||
return it;
|
return it;
|
||||||
|
@ -439,7 +440,8 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
|
||||||
|
|
||||||
flagAsModified();
|
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);
|
mListener->itemRemoved(item, count - toRemove);
|
||||||
|
|
||||||
// number of removed items
|
// number of removed items
|
||||||
|
|
|
@ -68,6 +68,9 @@ namespace MWWorld
|
||||||
|
|
||||||
static const std::string sGoldId;
|
static const std::string sGoldId;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ContainerStoreListener* mListener;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
MWWorld::CellRefList<ESM::Potion> potions;
|
MWWorld::CellRefList<ESM::Potion> potions;
|
||||||
|
@ -87,8 +90,6 @@ namespace MWWorld
|
||||||
///< Stores result of levelled item spawns. <(refId, spawningGroup), count>
|
///< Stores result of levelled item spawns. <(refId, spawningGroup), count>
|
||||||
/// This is used to restock levelled items(s) if the old item was sold.
|
/// This is used to restock levelled items(s) if the old item was sold.
|
||||||
|
|
||||||
ContainerStoreListener* mListener;
|
|
||||||
|
|
||||||
mutable float mCachedWeight;
|
mutable float mCachedWeight;
|
||||||
mutable bool mWeightUpToDate;
|
mutable bool mWeightUpToDate;
|
||||||
ContainerStoreIterator addImp (const Ptr& ptr, int count);
|
ContainerStoreIterator addImp (const Ptr& ptr, int count);
|
||||||
|
|
|
@ -141,6 +141,7 @@ void ESMStore::setUp(bool validateRecords)
|
||||||
mAttributes.setUp();
|
mAttributes.setUp();
|
||||||
mDialogs.setUp();
|
mDialogs.setUp();
|
||||||
mStatics.setUp();
|
mStatics.setUp();
|
||||||
|
mDoors.setUp();
|
||||||
|
|
||||||
if (validateRecords)
|
if (validateRecords)
|
||||||
validate();
|
validate();
|
||||||
|
|
|
@ -99,7 +99,8 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::InventoryStore::InventoryStore()
|
MWWorld::InventoryStore::InventoryStore()
|
||||||
: mListener(nullptr)
|
: ContainerStore()
|
||||||
|
, mInventoryListener(nullptr)
|
||||||
, mUpdatesEnabled (true)
|
, mUpdatesEnabled (true)
|
||||||
, mFirstAutoEquip(true)
|
, mFirstAutoEquip(true)
|
||||||
, mSelectedEnchantItem(end())
|
, mSelectedEnchantItem(end())
|
||||||
|
@ -111,7 +112,7 @@ MWWorld::InventoryStore::InventoryStore()
|
||||||
MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
|
MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
|
||||||
: ContainerStore (store)
|
: ContainerStore (store)
|
||||||
, mMagicEffects(store.mMagicEffects)
|
, mMagicEffects(store.mMagicEffects)
|
||||||
, mListener(store.mListener)
|
, mInventoryListener(store.mInventoryListener)
|
||||||
, mUpdatesEnabled(store.mUpdatesEnabled)
|
, mUpdatesEnabled(store.mUpdatesEnabled)
|
||||||
, mFirstAutoEquip(store.mFirstAutoEquip)
|
, mFirstAutoEquip(store.mFirstAutoEquip)
|
||||||
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
||||||
|
@ -124,6 +125,7 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
|
||||||
MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store)
|
MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store)
|
||||||
{
|
{
|
||||||
mListener = store.mListener;
|
mListener = store.mListener;
|
||||||
|
mInventoryListener = store.mInventoryListener;
|
||||||
mMagicEffects = store.mMagicEffects;
|
mMagicEffects = store.mMagicEffects;
|
||||||
mFirstAutoEquip = store.mFirstAutoEquip;
|
mFirstAutoEquip = store.mFirstAutoEquip;
|
||||||
mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes;
|
mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes;
|
||||||
|
@ -147,6 +149,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr,
|
||||||
autoEquip(actorPtr);
|
autoEquip(actorPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mListener)
|
||||||
|
mListener->itemAdded(itemPtr, count);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,25 +251,195 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) con
|
||||||
return mSlots[slot];
|
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<ESM::Weapon>()->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<int>(weaponSkillsLength); ++i)
|
||||||
|
{
|
||||||
|
int max = 0;
|
||||||
|
int maxWeaponSkill = -1;
|
||||||
|
|
||||||
|
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
|
||||||
|
{
|
||||||
|
int skillValue = actor.getClass().getSkill(actor, static_cast<int>(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<ESM::Weapon>()->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<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
|
||||||
|
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<std::vector<int>, 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 MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
|
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
|
||||||
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->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);
|
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)
|
for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter)
|
||||||
{
|
{
|
||||||
Ptr test = *iter;
|
Ptr test = *iter;
|
||||||
|
@ -289,12 +464,12 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
||||||
std::pair<std::vector<int>, bool> itemsSlots =
|
std::pair<std::vector<int>, bool> itemsSlots =
|
||||||
iter->getClass().getEquipmentSlots (*iter);
|
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<int>::const_iterator iter2 (itemsSlots.first.begin());
|
for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin());
|
||||||
iter2!=itemsSlots.first.end(); ++iter2)
|
iter2!=itemsSlots.first.end(); ++iter2)
|
||||||
{
|
{
|
||||||
// if true then it means slot is equipped already
|
// 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())
|
if (slots_.at (*iter2)!=end())
|
||||||
{
|
{
|
||||||
Ptr old = *slots_.at (*iter2);
|
Ptr old = *slots_.at (*iter2);
|
||||||
|
@ -362,98 +537,48 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const ESM::Skill::SkillEnum weaponSkills[] =
|
void MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_)
|
||||||
|
{
|
||||||
|
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter)
|
||||||
{
|
{
|
||||||
ESM::Skill::LongBlade,
|
if (iter->get<ESM::Armor>()->mBase->mData.mType != ESM::Armor::Shield)
|
||||||
ESM::Skill::Axe,
|
continue;
|
||||||
ESM::Skill::Spear,
|
if (iter->getClass().canBeEquipped(*iter, actor).first != 1)
|
||||||
ESM::Skill::ShortBlade,
|
continue;
|
||||||
ESM::Skill::Marksman,
|
if (iter->getClass().getItemHealth(*iter) <= 0)
|
||||||
ESM::Skill::BluntWeapon
|
continue;
|
||||||
};
|
std::pair<std::vector<int>, bool> shieldSlots =
|
||||||
const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]);
|
iter->getClass().getEquipmentSlots(*iter);
|
||||||
|
if (shieldSlots.first.empty())
|
||||||
bool weaponSkillVisited[weaponSkillsLength] = { false };
|
continue;
|
||||||
|
int slot = shieldSlots.first[0];
|
||||||
for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i)
|
const ContainerStoreIterator& shield = mSlots[slot];
|
||||||
{
|
if (shield != end()
|
||||||
int max = 0;
|
&& shield.getType() == Type_Armor && shield->get<ESM::Armor>()->mBase->mData.mType == ESM::Armor::Shield)
|
||||||
int maxWeaponSkill = -1;
|
|
||||||
|
|
||||||
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
|
|
||||||
{
|
{
|
||||||
int skillValue = actor.getClass().getSkill(actor, static_cast<int>(weaponSkills[j]));
|
if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter))
|
||||||
|
|
||||||
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;
|
continue;
|
||||||
|
|
||||||
const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
slots_[slot] = iter;
|
||||||
if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first)
|
|
||||||
{
|
|
||||||
std::pair<std::vector<int>, 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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
bool changed = false;
|
||||||
|
|
||||||
|
@ -476,50 +601,6 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor)
|
|
||||||
{
|
|
||||||
bool updated = false;
|
|
||||||
|
|
||||||
mUpdatesEnabled = false;
|
|
||||||
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter)
|
|
||||||
{
|
|
||||||
if (iter->get<ESM::Armor>()->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<std::vector<int>, 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<ESM::Armor>()->mBase->mData.mType == ESM::Armor::Shield)
|
|
||||||
{
|
|
||||||
if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
equip(slot, iter, actor);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
mUpdatesEnabled = true;
|
|
||||||
|
|
||||||
if (updated)
|
|
||||||
{
|
|
||||||
fireEquipmentChangedEvent(actor);
|
|
||||||
updateMagicEffects(actor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() const
|
const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() const
|
||||||
{
|
{
|
||||||
return mMagicEffects;
|
return mMagicEffects;
|
||||||
|
@ -532,7 +613,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Delay update until the listener is set up
|
// Delay update until the listener is set up
|
||||||
if (!mListener)
|
if (!mInventoryListener)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mMagicEffects = MWMechanics::MagicEffects();
|
mMagicEffects = MWMechanics::MagicEffects();
|
||||||
|
@ -603,7 +684,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
|
||||||
// During first auto equip, we don't play any sounds.
|
// During first auto equip, we don't play any sounds.
|
||||||
// Basically we don't want sounds when the actor is first loaded,
|
// Basically we don't want sounds when the actor is first loaded,
|
||||||
// the items should appear as if they'd always been equipped.
|
// the items should appear as if they'd always been equipped.
|
||||||
mListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip);
|
mInventoryListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (magnitude)
|
if (magnitude)
|
||||||
|
@ -737,6 +818,9 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor
|
||||||
mSelectedEnchantItem = end();
|
mSelectedEnchantItem = end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mListener)
|
||||||
|
mListener->itemRemoved(item, retCount);
|
||||||
|
|
||||||
return retCount;
|
return retCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,12 +906,12 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(con
|
||||||
|
|
||||||
MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getInvListener()
|
MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getInvListener()
|
||||||
{
|
{
|
||||||
return mListener;
|
return mInventoryListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::setInvListener(InventoryStoreListener *listener, const Ptr& actor)
|
void MWWorld::InventoryStore::setInvListener(InventoryStoreListener *listener, const Ptr& actor)
|
||||||
{
|
{
|
||||||
mListener = listener;
|
mInventoryListener = listener;
|
||||||
updateMagicEffects(actor);
|
updateMagicEffects(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,8 +919,8 @@ void MWWorld::InventoryStore::fireEquipmentChangedEvent(const Ptr& actor)
|
||||||
{
|
{
|
||||||
if (!mUpdatesEnabled)
|
if (!mUpdatesEnabled)
|
||||||
return;
|
return;
|
||||||
if (mListener)
|
if (mInventoryListener)
|
||||||
mListener->equipmentChanged();
|
mInventoryListener->equipmentChanged();
|
||||||
|
|
||||||
// if player, update inventory window
|
// if player, update inventory window
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace MWWorld
|
||||||
|
|
||||||
MWMechanics::MagicEffects mMagicEffects;
|
MWMechanics::MagicEffects mMagicEffects;
|
||||||
|
|
||||||
InventoryStoreListener* mListener;
|
InventoryStoreListener* mInventoryListener;
|
||||||
|
|
||||||
// Enables updates of magic effects and actor model whenever items are equipped or unequipped.
|
// Enables updates of magic effects and actor model whenever items are equipped or unequipped.
|
||||||
// This is disabled during autoequip to avoid excessive updates
|
// This is disabled during autoequip to avoid excessive updates
|
||||||
|
@ -94,6 +94,10 @@ namespace MWWorld
|
||||||
|
|
||||||
TSlots mSlots;
|
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")
|
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
|
||||||
ContainerStoreIterator mSelectedEnchantItem;
|
ContainerStoreIterator mSelectedEnchantItem;
|
||||||
|
|
||||||
|
@ -164,9 +168,6 @@ namespace MWWorld
|
||||||
void autoEquip (const MWWorld::Ptr& actor);
|
void autoEquip (const MWWorld::Ptr& actor);
|
||||||
///< Auto equip items according to stats and item value.
|
///< 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;
|
const MWMechanics::MagicEffects& getMagicEffects() const;
|
||||||
///< Return magic effects from worn items.
|
///< Return magic effects from worn items.
|
||||||
|
|
||||||
|
|
|
@ -1059,11 +1059,11 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
// Load default marker definitions, if game files do not have them for some reason
|
// Load default marker definitions, if game files do not have them for some reason
|
||||||
std::pair<std::string, std::string> markers[] = {
|
std::pair<std::string, std::string> markers[] = {
|
||||||
std::make_pair("divinemarker", "marker_divine.nif"),
|
std::make_pair("DivineMarker", "marker_divine.nif"),
|
||||||
std::make_pair("doormarker", "marker_arrow.nif"),
|
std::make_pair("DoorMarker", "marker_arrow.nif"),
|
||||||
std::make_pair("northmarker", "marker_north.nif"),
|
std::make_pair("NorthMarker", "marker_north.nif"),
|
||||||
std::make_pair("templemarker", "marker_temple.nif"),
|
std::make_pair("TempleMarker", "marker_temple.nif"),
|
||||||
std::make_pair("travelmarker", "marker_travel.nif")
|
std::make_pair("TravelMarker", "marker_travel.nif")
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const std::pair<std::string, std::string> marker : markers)
|
for (const std::pair<std::string, std::string> marker : markers)
|
||||||
|
@ -1080,6 +1080,28 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Store<ESM::Door>::setUp()
|
||||||
|
{
|
||||||
|
// Load default Door type marker definitions
|
||||||
|
std::pair<std::string, std::string> markers[] = {
|
||||||
|
std::make_pair("PrisonMarker", "marker_prison.nif")
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const std::pair<std::string, std::string> 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<typename Static::iterator, bool> ret = mStatic.insert(std::make_pair(marker.first, newMarker));
|
||||||
|
if (ret.first != mStatic.end())
|
||||||
|
{
|
||||||
|
mShared.push_back(&ret.first->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline RecordId Store<ESM::Dialogue>::load(ESM::ESMReader &esm) {
|
inline RecordId Store<ESM::Dialogue>::load(ESM::ESMReader &esm) {
|
||||||
// The original letter case of a dialogue ID is saved, because it's printed
|
// The original letter case of a dialogue ID is saved, because it's printed
|
||||||
|
|
|
@ -31,11 +31,13 @@ void Wizard::ExistingInstallationPage::initializePage()
|
||||||
// Hide the default item if there are installations to choose from
|
// Hide the default item if there are installations to choose from
|
||||||
installationsList->item(0)->setHidden(!paths.isEmpty());
|
installationsList->item(0)->setHidden(!paths.isEmpty());
|
||||||
|
|
||||||
foreach (const QString &path, paths) {
|
foreach (const QString &path, paths)
|
||||||
QListWidgetItem *item = new QListWidgetItem(path);
|
{
|
||||||
|
|
||||||
if (installationsList->findItems(path, Qt::MatchExactly).isEmpty())
|
if (installationsList->findItems(path, Qt::MatchExactly).isEmpty())
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(path);
|
||||||
installationsList->addItem(item);
|
installationsList->addItem(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(installationsList, SIGNAL(currentTextChanged(QString)),
|
connect(installationsList, SIGNAL(currentTextChanged(QString)),
|
||||||
|
|
|
@ -168,7 +168,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
{
|
{
|
||||||
if (column >=0 && column <=EsmFile::FileProperty_GameFile)
|
if (column >=0 && column <=EsmFile::FileProperty_GameFile)
|
||||||
return file->fileProperty(static_cast<const EsmFile::FileProperty>(column));
|
return file->fileProperty(static_cast<EsmFile::FileProperty>(column));
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,17 +86,6 @@ namespace DetourNavigator
|
||||||
return static_cast<std::size_t*>(ptr) + 1;
|
return static_cast<std::size_t*>(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<std::uintptr_t>(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<void*>(aligned);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace DetourNavigator
|
||||||
std::size_t space = mStack.size() - getUsedSize();
|
std::size_t space = mStack.size() - getUsedSize();
|
||||||
void* top = mTop;
|
void* top = mTop;
|
||||||
const auto itemSize = 2 * sizeof(std::size_t) + size;
|
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;
|
return nullptr;
|
||||||
setTempPtrBufferType(top, BufferType_temp);
|
setTempPtrBufferType(top, BufferType_temp);
|
||||||
setTempPtrPrev(top, mPrev);
|
setTempPtrPrev(top, mPrev);
|
||||||
|
|
|
@ -22,6 +22,21 @@ struct Door
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< 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
|
#endif
|
||||||
|
|
|
@ -134,9 +134,9 @@ public:
|
||||||
{
|
{
|
||||||
state->bindVertexBufferObject(bufferobject);
|
state->bindVertexBufferObject(bufferobject);
|
||||||
|
|
||||||
glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr);
|
glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast<char*>(0));
|
||||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)nullptr + 12);
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), static_cast<char*>(0) + 12);
|
||||||
glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr + 16);
|
glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast<char*>(0) + 16);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -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",
|
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 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<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
|
reservedNames = std::vector<std::string>(reserved, reserved + sizeof(reserved)/sizeof(reserved[0]));
|
||||||
|
|
||||||
for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)
|
for (unsigned int i=0; i<sizeof(reserved)/sizeof(reserved[0]); ++i)
|
||||||
|
|
|
@ -171,9 +171,20 @@ followers attack on sight
|
||||||
Make player followers and escorters start combat with enemies who have started combat with them or the player.
|
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.
|
Otherwise they wait for the enemies or the player to do an attack first.
|
||||||
Please note this setting has not been extensively tested and could have side effects with certain quests.
|
Please note this setting has not been extensively tested and could have side effects with certain quests.
|
||||||
|
|
||||||
This setting can be toggled in Advanced tab of the launcher.
|
This setting can be toggled in Advanced tab of the launcher.
|
||||||
|
|
||||||
|
weapon sheathing
|
||||||
|
----------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
If this setting is true, OpenMW will utilize weapon sheathing-compatible assets to display holstered weapons.
|
||||||
|
|
||||||
|
To make use of this, you need to have an xbase_anim_sh.nif file with weapon bones that will be injected into the skeleton.
|
||||||
|
Additional _sh suffix models are not essential for weapon sheathing to work but will act as quivers or scabbards for the weapons they correspond to.
|
||||||
|
|
||||||
use additional anim sources
|
use additional anim sources
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
2
extern/recastnavigation/.id
vendored
2
extern/recastnavigation/.id
vendored
|
@ -1 +1 @@
|
||||||
7bfd9a1d4caccf61e0485b2b05b29966348a8b39
|
3087e805b02d5eb8fff7851234fa2b3f71290eba
|
||||||
|
|
|
@ -66,6 +66,7 @@ void rcFree(void* ptr);
|
||||||
/// and STL.
|
/// and STL.
|
||||||
struct rcNewTag {};
|
struct rcNewTag {};
|
||||||
inline void* operator new(size_t, const rcNewTag&, void* p) { return p; }
|
inline void* operator new(size_t, const rcNewTag&, void* p) { return p; }
|
||||||
|
inline void operator delete(void*, const rcNewTag&, void*) {}
|
||||||
|
|
||||||
/// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero.
|
/// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero.
|
||||||
/// MSVC2010 has a bug where ssize_t is unsigned (!!!).
|
/// MSVC2010 has a bug where ssize_t is unsigned (!!!).
|
||||||
|
|
|
@ -41,8 +41,7 @@ solution "recastnavigation"
|
||||||
-- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead.
|
-- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead.
|
||||||
-- Disable:
|
-- Disable:
|
||||||
-- * C4351: new behavior for array initialization
|
-- * C4351: new behavior for array initialization
|
||||||
-- * C4291: no matching operator delete found; we don't use exceptions, so doesn't matter
|
buildoptions { "/W3", "/wd4351" }
|
||||||
buildoptions { "/W3", "/wd4351", "/wd4291" }
|
|
||||||
|
|
||||||
filter "platforms:Win32"
|
filter "platforms:Win32"
|
||||||
architecture "x32"
|
architecture "x32"
|
||||||
|
@ -170,7 +169,7 @@ project "RecastDemo"
|
||||||
}
|
}
|
||||||
postbuildcommands {
|
postbuildcommands {
|
||||||
-- Copy the SDL2 dll to the Bin folder.
|
-- Copy the SDL2 dll to the Bin folder.
|
||||||
'{COPY} "%{wks.location}../../Contrib/SDL/lib/%{cfg.architecture:gsub("x86_64", "x64")}/SDL2.dll" "%{cfg.targetdir}"'
|
'{COPY} "%{path.getabsolute("Contrib/SDL/lib/" .. cfg.architecture:gsub("x86_64", "x64") .. "/SDL2.dll")}" "%{cfg.targetdir}"'
|
||||||
}
|
}
|
||||||
|
|
||||||
-- mac includes and libs
|
-- mac includes and libs
|
||||||
|
|
|
@ -117,69 +117,69 @@ fallback=FontColor_color_negative,200,60,30
|
||||||
fallback=FontColor_color_count,223,201,159
|
fallback=FontColor_color_count,223,201,159
|
||||||
|
|
||||||
# leveling
|
# leveling
|
||||||
fallback=Level_Up_Level2,You've just realized that there is more to the life than you thought, the last few days have opened your eyes. An uncertain future awaits you.
|
fallback=Level_Up_Level2,The last few days have opened your eyes: you realize now that there is more to the life than you thought. An uncertain future awaits you.
|
||||||
fallback=Level_Up_Level3,Hard work and determination have served you well, keep this up and you'll be bound for great things.
|
fallback=Level_Up_Level3,You finally come to understand that great doings start from hard work and determination.
|
||||||
fallback=Level_Up_Level4,It's as clear as day to you now, all that time you pissed away in your youth. This... this is life and all the experience you've gained giving you the tools you need to succeed in life.
|
fallback=Level_Up_Level4,After reminiscing upon all that time you lost in your youth, it's as clear as day to you now: this - this is the life, and all the experience you gain gives you the tools you need to succeed in it.
|
||||||
fallback=Level_Up_Level5,Things seem to be working out for you, the world is opening up as you become more capable.
|
fallback=Level_Up_Level5,Things seem to be working out for you. As you become more capable, the world is opening up.
|
||||||
fallback=Level_Up_Level6,There is so much more to learn but with every push you gain more knowledge, only increasing your thirst for it.
|
fallback=Level_Up_Level6,With every push you did to gain knowledge, and yet your thirst for it increased, you realize there is so much more to learn still.
|
||||||
fallback=Level_Up_Level7,You've dug deep within yourself, thinking you'll finally be who you thought you would be... but there is more and you keep digging.
|
fallback=Level_Up_Level7,You dig deep within yourself, trying to finally become the one who you thought you would be... but there's much to keep digging through.
|
||||||
fallback=Level_Up_Level8,Success doesn't come easily, but you make it appear that way to others.
|
fallback=Level_Up_Level8,Success doesn't come easily, but you make it appear that way to others.
|
||||||
fallback=Level_Up_Level9,Everything is becoming second nature to you but also just a bit more dangerous.
|
fallback=Level_Up_Level9,Everything may slowly become a second nature to you, but it also can turn just a bit more dangerous.
|
||||||
fallback=Level_Up_Level10,Nothing in life is easy or it wouldn't be worth the blood, sweat and tears.
|
fallback=Level_Up_Level10,Nothing in life is easy, or it wouldn't have been worth the blood, sweat and tears.
|
||||||
fallback=Level_Up_Level11,Working smarter, not harder is something even a barbarian could benefit from.
|
fallback=Level_Up_Level11,Working smarter, not harder is something even a barbarian could benefit from.
|
||||||
fallback=Level_Up_Level12,Some would call you crazy for keeping at it, but you know better. It'll all pay off, it already has.
|
fallback=Level_Up_Level12,Perhaps some would call you delusional for keeping at it, but you know better. Your dilegence has already paid off, after all.
|
||||||
fallback=Level_Up_Level13,One nights sleep was all the difference between between something being difficult and then it being easy.
|
fallback=Level_Up_Level13,One night's sleep is all the difference between something being difficult and something being easy.
|
||||||
fallback=Level_Up_Level14,Waking up today was the best day ever for you, you await more days like these in your future.
|
fallback=Level_Up_Level14,After waking up, you wonder: could today be the best day in your life? You never know.
|
||||||
fallback=Level_Up_Level15,Ouch! You lean back feeling your whole body for what seems the first time. You'll be more mindful in the future, you only have by one life.
|
fallback=Level_Up_Level15,Ouch! You lean back feeling your whole body for what seems the first time. You'll be more mindful in the future -- you can only live once.
|
||||||
fallback=Level_Up_Level16,Trusting your instincts have gotten you this far, but you'll need to be smarter to keep from being dead.
|
fallback=Level_Up_Level16,Trusting your instincts has gotten you this far, but now it's clear to you that you need to be smarter to survive.
|
||||||
fallback=Level_Up_Level17,You're forging your spirit in the crucible that is experience. Be ever vigilant.
|
fallback=Level_Up_Level17,You're forging your spirit in the crucible that is experience. Be ever vigilant.
|
||||||
fallback=Level_Up_Level18,The frustrations of the day before melt away as you wake up, today is a new day.
|
fallback=Level_Up_Level18,The frustrations of the day before vanish as you wake up. Today is a new day.
|
||||||
fallback=Level_Up_Level19,Today isn't yesterday, you feel it deep inside but you know that there is still more to this life.
|
fallback=Level_Up_Level19,Today isn't yesterday, you feel that deep inside. You know that there is still more to this life.
|
||||||
fallback=Level_Up_Level20,Luck has never been a factor in your success, just look at the scars on your body and the trails you've endured.
|
fallback=Level_Up_Level20,Luck has never been a factor in your success -- that's obvious from the scars on your body and from the number of trails you've endured.
|
||||||
fallback=Level_Up_Default,Through sheer force of will, you push onwards and upwards. The toil of your persistence has paid off.
|
fallback=Level_Up_Default,Through sheer force of will, you push onwards and upwards. The toil of your persistence is paying off.
|
||||||
|
|
||||||
# character creation
|
# character creation
|
||||||
fallback=Question_1_Question,Before you lies some kind of creation you never seen before, it's hind leg caught in a hunter's trap. You can tell that its leg is broken.
|
fallback=Question_1_Question,Before you lies a creature you have never seen before. Its hind leg is caught in a hunter's trap, and you can tell that the leg is broken. What will you do?
|
||||||
fallback=Question_1_AnswerOne,Pull out you knife for a short and merciful kill.
|
fallback=Question_1_AnswerOne,Pull out you knife for a short and merciful kill.
|
||||||
fallback=Question_1_AnswerTwo,Reach into your herbal pouch to find something to ease its suffering before putting it to sleep.
|
fallback=Question_1_AnswerTwo,Reach into your herbal pouch to find something to ease its suffering before putting it to sleep.
|
||||||
fallback=Question_1_AnswerThree,Leave it alone but take the time to observe and learn from this new creation
|
fallback=Question_1_AnswerThree,Leave the leg alone, but take the time to observe and learn from this odd creature.
|
||||||
fallback=Question_1_Sound,Voice\CharGen\QA1.mp3
|
fallback=Question_1_Sound,Voice\CharGen\QA1.mp3
|
||||||
fallback=Question_2_Question,Your mother has given everyone a list of choice, pick one.
|
fallback=Question_2_Question,Your mother suggests you to help with work around your family household. Do you decide to...
|
||||||
fallback=Question_2_AnswerOne,The fence posts need replacing, help your father?
|
fallback=Question_2_AnswerOne,Help your father with fence post replacement?
|
||||||
fallback=Question_2_AnswerTwo,Tonight's supper needs a few herbs, head out into the forest to collect them?
|
fallback=Question_2_AnswerTwo,Collect a few herbs in the forest for tonight's supper?
|
||||||
fallback=Question_2_AnswerThree,Fish aren't going find their way into the kitchen without your help. Go fishing?
|
fallback=Question_2_AnswerThree,Help the fish to find their way into the kitchen?
|
||||||
fallback=Question_2_Sound,Voice\CharGen\QA2.mp3
|
fallback=Question_2_Sound,Voice\CharGen\QA2.mp3
|
||||||
fallback=Question_3_Question,One of your brother teases you mercilessly in front of everyone about embarrassing details you would rather be forgotten.
|
fallback=Question_3_Question,Your brother teases you mercilessly in front of everyone with embarrassing details you would rather have forgotten.
|
||||||
fallback=Question_3_AnswerOne,Give him a black-eye and dare him to keep it up?
|
fallback=Question_3_AnswerOne,Give him a black-eye and dare him to keep it up.
|
||||||
fallback=Question_3_AnswerTwo,Beat him to the punch and play it up, if you can control the negative then he can't embarrass you?
|
fallback=Question_3_AnswerTwo,Beat him to the punch and play it up -- if you can control the negative, then he can't embarrass you.
|
||||||
fallback=Question_3_AnswerThree,Wait until he sleeps, put his hand in a bowl of water and call everyone around the next day to see the results?
|
fallback=Question_3_AnswerThree,Wait until he sleeps, put his hand in a bowl of water and call everyone around the next day to see the results.
|
||||||
fallback=Question_3_Sound,Voice\CharGen\QA3.mp3
|
fallback=Question_3_Sound,Voice\CharGen\QA3.mp3
|
||||||
fallback=Question_4_Question,Rumor has it that the King's security console has a new tool at their disposal for sniffing out the truth, people able to read minds.
|
fallback=Question_4_Question,Rumor has it that the King's security console has a new tool at their disposal for sniffing out the truth, which could be used for reading minds.
|
||||||
fallback=Question_4_AnswerOne,You recoil at the thought of someone being able to read your thoughts. It's not that you think something that you'll ever act on it?
|
fallback=Question_4_AnswerOne,You recoil at the thought of someone being able to read your thoughts. It's not that you think something that you'll ever act on it.
|
||||||
fallback=Question_4_AnswerTwo,For those who have done nothing, there is nothing to fear. This is just yet another tool for sniffing out thieves, murderers and plots against the crown?
|
fallback=Question_4_AnswerTwo,For those who have done nothing, there is nothing to fear. This is just yet another utility for catching thieves, murderers and plots against the crown.
|
||||||
fallback=Question_4_AnswerThree,While you loath the idea of someone reading your mind, you accept that it does have its uses if tempered by law and common sense. You wouldn't believe the mind of a madman for example?
|
fallback=Question_4_AnswerThree,While you loath the idea of someone reading your mind, you accept that it does have its uses if tempered by law and common sense. You wouldn't believe the mind of a madman.
|
||||||
fallback=Question_4_Sound,Voice\CharGen\QA4.mp3
|
fallback=Question_4_Sound,Voice\CharGen\QA4.mp3
|
||||||
fallback=Question_5_Question,You're off to market for supplies. You notice that one of the merchants had given you too much back in change.
|
fallback=Question_5_Question,You are returning home from the market after acquiring supplies, and notice that one of the merchants had given you too much back in change.
|
||||||
fallback=Question_5_AnswerOne,How dreadful, what if it was you. You head back to the merchant?
|
fallback=Question_5_AnswerOne,How dreadful, what if it was you? You head back to the merchant.
|
||||||
fallback=Question_5_AnswerTwo,Happy days indeed, you put that extra money towards the needs of your family?
|
fallback=Question_5_AnswerTwo,Happy days indeed. Put that extra money towards the needs of your family?
|
||||||
fallback=Question_5_AnswerThree,You win some and you lose some. In this case you won and they lost, the oversight is the merchant's problem, not yours?
|
fallback=Question_5_AnswerThree,You win some and you lose some. In this case you have won and they have lost, and the oversight is the merchant's problem, not yours, right?
|
||||||
fallback=Question_5_Sound,Voice\CharGen\QA5.mp3
|
fallback=Question_5_Sound,Voice\CharGen\QA5.mp3
|
||||||
fallback=Question_6_Question,While at market, a noble yells out to the city watch about a theft. As you turn around a man bumps into you and drops a sack, startled he darts into the crowd.
|
fallback=Question_6_Question,While at market, a noble yells out to the city watch about a theft. As you turn around a man bumps into you and drops a sack, startled he darts into the crowd.
|
||||||
fallback=Question_6_AnswerOne,You pick up the sack and head over to the noble to return his property?
|
fallback=Question_6_AnswerOne,You pick up the sack and head over to the noble to return his property?
|
||||||
fallback=Question_6_AnswerTwo,Just walk away, the last thing you need is someone thinking you had anything to do with it?
|
fallback=Question_6_AnswerTwo,Just walk away, the last thing you need is someone thinking you had anything to do with it?
|
||||||
fallback=Question_6_AnswerThree,Finders, keepers... you whistle as you walk away?
|
fallback=Question_6_AnswerThree,Finders, keepers... you whistle as you walk away with the bag hidden?
|
||||||
fallback=Question_6_Sound,Voice\CharGen\QA6.mp3
|
fallback=Question_6_Sound,Voice\CharGen\QA6.mp3
|
||||||
fallback=Question_7_Question,If it's one thing you hate, it's cleaning out the stalls. It has to be done before sunset before the critters return. On your way there an old friend greets you and offers to help if you promise to help them in the future.
|
fallback=Question_7_Question,If it's one thing you hate, it's cleaning out the stalls. It has to be done before sunset before the critters return. On your way there an old friend greets you and offers to help if you promise to help them in the future.
|
||||||
fallback=Question_7_AnswerOne,You thank him for the offer but would rather get it over with and not make promises you can't keep?
|
fallback=Question_7_AnswerOne,You thank him for the offer but would rather get it over with and not make promises you can't keep?
|
||||||
fallback=Question_7_AnswerTwo,You reason that two pairs of hands are better than one regardless of the task?
|
fallback=Question_7_AnswerTwo,You reason that two pairs of hands are better than one regardless of the task?
|
||||||
fallback=Question_7_AnswerThree,Sounds great, anything is better than cleaning the stalls.
|
fallback=Question_7_AnswerThree,You say that it sounds great and anything is better than cleaning the stalls?
|
||||||
fallback=Question_7_Sound,Voice\CharGen\QA7.mp3
|
fallback=Question_7_Sound,Voice\CharGen\QA7.mp3
|
||||||
fallback=Question_8_Question,You just climbed down ladder from working on the roof. You're mother thanks you for the hard work but just at the moment you notice the hammer about to fall down on her head.
|
fallback=Question_8_Question,You just climbed down the ladder after working on the roof. Your mother thanks you for the hard work but just at the moment you notice that a hammer is about to fall down on her head.
|
||||||
fallback=Question_8_AnswerOne,You lunge at your mother, pushing her out the way while the hammer falls on top of you?
|
fallback=Question_8_AnswerOne,You lunge at your mother, pushing her out the way while the hammer falls on top of you?
|
||||||
fallback=Question_8_AnswerTwo,Use the ladder to intercept the hammer before it lands on her?
|
fallback=Question_8_AnswerTwo,Try to use the ladder to intercept the hammer before it lands on her?
|
||||||
fallback=Question_8_AnswerThree,Warn her to take a step back?
|
fallback=Question_8_AnswerThree,Warn her to take a step back?
|
||||||
fallback=Question_8_Sound,Voice\CharGen\QA8.mp3
|
fallback=Question_8_Sound,Voice\CharGen\QA8.mp3
|
||||||
fallback=Question_9_Question,It's the end of the week and you just got your wages for your hard work. You decide to take the quick way back home, darting into a alley only to be confronted by ruffians who demand that you empty your pockets.
|
fallback=Question_9_Question,It's the end of the week, and you have just got your wages for your hard work. You decide to take the quick way back home, darting into a alley only to be confronted by ruffians who demand that you empty your pockets.
|
||||||
fallback=Question_9_AnswerOne,You tell them to go pack sand, planting your feet and raising your fists?
|
fallback=Question_9_AnswerOne,You tell them to go pack sand, planting your feet and raising your fists?
|
||||||
fallback=Question_9_AnswerTwo,Acting dejected, you turn your wages over. You know that you can count on your friends to help you track these brigands down and recover what's yours?
|
fallback=Question_9_AnswerTwo,Acting dejected, you turn your wages over. You know that you can count on your friends to help you track these brigands down and recover what's yours?
|
||||||
fallback=Question_9_AnswerThree,Tossing the sack into the air, you charge the leader who's attention is squarely focused on the coins in flight?
|
fallback=Question_9_AnswerThree,Tossing the sack into the air, you charge the leader who's attention is squarely focused on the coins in flight?
|
||||||
|
|
|
@ -239,6 +239,9 @@ barter disposition change is permanent = false
|
||||||
# 2 means werewolves are ignored)
|
# 2 means werewolves are ignored)
|
||||||
strength influences hand to hand = 0
|
strength influences hand to hand = 0
|
||||||
|
|
||||||
|
# Render holstered weapons (with quivers and scabbards), requires modded assets
|
||||||
|
weapon sheathing = false
|
||||||
|
|
||||||
[General]
|
[General]
|
||||||
|
|
||||||
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
||||||
|
|
Loading…
Reference in a new issue