Merge remote-tracking branch 'upstream/master' into terrain

loadfix
cc9cii 10 years ago
commit 406135ee96

@ -1,37 +1,26 @@
os:
- linux
- osx
language: cpp language: cpp
compiler:
- gcc
branches: branches:
only: only:
- master - master
- /openmw-.*$/ - /openmw-.*$/
before_install: before_install:
- pwd - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi
- echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi
- echo "yes" | sudo apt-add-repository ppa:openmw/openmw
- sudo apt-get update -qq
- sudo apt-get install -qq libgtest-dev google-mock
- sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libboost-wave-dev
- sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
- sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
- sudo mkdir /usr/src/gtest/build
- cd /usr/src/gtest/build
- sudo cmake .. -DBUILD_SHARED_LIBS=1
- sudo make -j4
- sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
- sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
before_script: before_script:
- cd - - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_script.linux.sh; fi
- mkdir build - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
- cd build
- cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
script: script:
- cd ./build
- make -j4 - make -j4
after_script: after_script:
- ./openmw_test_suite - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
notifications: notifications:
recipients: recipients:
- lgromanowski+travis.ci@gmail.com - lgromanowski+travis.ci@gmail.com
- corrmage+travis-ci@gmail.com
email: email:
on_success: change on_success: change
on_failure: always on_failure: always

@ -0,0 +1,18 @@
#!/bin/sh
export CXX=g++
export CC=gcc
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
echo "yes" | sudo apt-add-repository ppa:openmw/openmw
sudo apt-get update -qq
sudo apt-get install -qq libgtest-dev google-mock
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libboost-wave-dev
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
sudo mkdir /usr/src/gtest/build
cd /usr/src/gtest/build
sudo cmake .. -DBUILD_SHARED_LIBS=1
sudo make -j4
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so

@ -0,0 +1,9 @@
#!/bin/sh
export CXX=clang++
export CC=clang
brew tap openmw/openmw
brew update
brew unlink boost
brew install cmake openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg pkg-config qt unshield

@ -0,0 +1,5 @@
#!/bin/sh
mkdir build
cd build
cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE

@ -0,0 +1,5 @@
#!/bin/sh
mkdir build
cd build
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..

@ -26,7 +26,7 @@ opencs_units (model/world
opencs_units_noqt (model/world opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection universalid record commands columnbase scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope landtexture land
) )
opencs_hdrs_noqt (model/world opencs_hdrs_noqt (model/world
@ -76,7 +76,7 @@ opencs_units (view/widget
opencs_units (view/render opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget previewwidget terrainstorage
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render

@ -539,6 +539,16 @@ CSMWorld::IdCollection<ESM::DebugProfile>& CSMWorld::Data::getDebugProfiles()
return mDebugProfiles; return mDebugProfiles;
} }
const CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand() const
{
return mLand;
}
const CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures() const
{
return mLandTextures;
}
const CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) const const CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) const
{ {
return mResourcesManager.get (id.getType()); return mResourcesManager.get (id.getType());
@ -573,8 +583,11 @@ void CSMWorld::Data::merge()
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project) int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
{ {
delete mReader; // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
boost::shared_ptr<ESM::ESMReader> ptr(mReader);
mReaders.push_back(ptr);
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear(); mRefLoadCache.clear();
@ -598,8 +611,11 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
if (!mReader->hasMoreRecs()) if (!mReader->hasMoreRecs())
{ {
delete mReader; // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
boost::shared_ptr<ESM::ESMReader> ptr(mReader);
mReaders.push_back(ptr);
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear(); mRefLoadCache.clear();
return true; return true;
@ -626,6 +642,9 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
case ESM::REC_ENCH: mEnchantments.load (*mReader, mBase); break; case ESM::REC_ENCH: mEnchantments.load (*mReader, mBase); break;
case ESM::REC_BODY: mBodyParts.load (*mReader, mBase); break; case ESM::REC_BODY: mBodyParts.load (*mReader, mBase); break;
case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break;
case ESM::REC_LAND: mLand.load(*mReader, mBase); break;
case ESM::REC_CELL: case ESM::REC_CELL:
{ {
mCells.load (*mReader, mBase); mCells.load (*mReader, mBase);
@ -775,7 +794,9 @@ bool CSMWorld::Data::hasId (const std::string& id) const
getCells().searchId (id)!=-1 || getCells().searchId (id)!=-1 ||
getEnchantments().searchId (id)!=-1 || getEnchantments().searchId (id)!=-1 ||
getBodyParts().searchId (id)!=-1 || getBodyParts().searchId (id)!=-1 ||
getReferenceables().searchId (id)!=-1; getReferenceables().searchId (id)!=-1 ||
getLand().searchId (id) != -1 ||
getLandTextures().searchId (id) != -1;
} }
int CSMWorld::Data::count (RecordBase::State state) const int CSMWorld::Data::count (RecordBase::State state) const
@ -795,7 +816,9 @@ int CSMWorld::Data::count (RecordBase::State state) const
count (state, mCells) + count (state, mCells) +
count (state, mEnchantments) + count (state, mEnchantments) +
count (state, mBodyParts) + count (state, mBodyParts) +
count (state, mReferenceables); count (state, mReferenceables) +
count (state, mLand) +
count (state, mLandTextures);
} }
void CSMWorld::Data::setDescription (const std::string& description) void CSMWorld::Data::setDescription (const std::string& description)
@ -838,6 +861,8 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
appendIds (ids, mEnchantments, listDeleted); appendIds (ids, mEnchantments, listDeleted);
appendIds (ids, mBodyParts, listDeleted); appendIds (ids, mBodyParts, listDeleted);
appendIds (ids, mReferenceables, listDeleted); appendIds (ids, mReferenceables, listDeleted);
appendIds (ids, mLand, listDeleted);
appendIds (ids, mLandTextures, listDeleted);
std::sort (ids.begin(), ids.end()); std::sort (ids.begin(), ids.end());

@ -33,6 +33,8 @@
#include "idcollection.hpp" #include "idcollection.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "cell.hpp" #include "cell.hpp"
#include "land.hpp"
#include "landtexture.hpp"
#include "refidcollection.hpp" #include "refidcollection.hpp"
#include "refcollection.hpp" #include "refcollection.hpp"
#include "infocollection.hpp" #include "infocollection.hpp"
@ -74,6 +76,8 @@ namespace CSMWorld
InfoCollection mTopicInfos; InfoCollection mTopicInfos;
InfoCollection mJournalInfos; InfoCollection mJournalInfos;
IdCollection<Cell> mCells; IdCollection<Cell> mCells;
IdCollection<LandTexture> mLandTextures;
IdCollection<Land> mLand;
RefIdCollection mReferenceables; RefIdCollection mReferenceables;
RefCollection mRefs; RefCollection mRefs;
IdCollection<ESM::Filter> mFilters; IdCollection<ESM::Filter> mFilters;
@ -88,6 +92,8 @@ namespace CSMWorld
bool mProject; bool mProject;
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache; std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
// not implemented // not implemented
Data (const Data&); Data (const Data&);
Data& operator= (const Data&); Data& operator= (const Data&);
@ -195,6 +201,10 @@ namespace CSMWorld
IdCollection<ESM::DebugProfile>& getDebugProfiles(); IdCollection<ESM::DebugProfile>& getDebugProfiles();
const IdCollection<CSMWorld::Land>& getLand() const;
const IdCollection<CSMWorld::LandTexture>& getLandTextures() const;
/// Throws an exception, if \a id does not match a resources list. /// Throws an exception, if \a id does not match a resources list.
const Resources& getResources (const UniversalId& id) const; const Resources& getResources (const UniversalId& id) const;
@ -247,4 +257,4 @@ namespace CSMWorld
}; };
} }
#endif #endif

@ -29,7 +29,7 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
{ {
if (role!=Qt::DisplayRole && role!=Qt::EditRole) if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0)
return QVariant(); return QVariant();
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable()) if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
@ -229,4 +229,4 @@ bool CSMWorld::IdTable::isDeleted (const std::string& id) const
int CSMWorld::IdTable::getColumnId(int column) const int CSMWorld::IdTable::getColumnId(int column) const
{ {
return mIdCollection->getColumn(column).getId(); return mIdCollection->getColumn(column).getId();
} }

@ -0,0 +1,28 @@
#include "land.hpp"
#include <sstream>
namespace CSMWorld
{
Land::Land()
{
mLand.reset(new ESM::Land());
}
void Land::load(ESM::ESMReader &esm)
{
mLand->load(esm);
std::ostringstream stream;
stream << "#" << mLand->mX << " " << mLand->mY;
mId = stream.str();
}
void Land::blank()
{
/// \todo
}
}

@ -0,0 +1,29 @@
#ifndef CSM_WORLD_LAND_H
#define CSM_WORLD_LAND_H
#include <string>
#include <boost/shared_ptr.hpp>
#include <components/esm/loadland.hpp>
namespace CSMWorld
{
/// \brief Wrapper for Land record. Encodes X and Y cell index in the ID.
///
/// \todo Add worldspace support to the Land record.
/// \todo Add a proper copy constructor (currently worked around using shared_ptr)
struct Land
{
Land();
boost::shared_ptr<ESM::Land> mLand;
std::string mId;
/// Loads the metadata and ID
void load (ESM::ESMReader &esm);
void blank();
};
}
#endif

@ -0,0 +1,21 @@
#include "landtexture.hpp"
#include <components/esm/esmreader.hpp>
namespace CSMWorld
{
void LandTexture::load(ESM::ESMReader &esm)
{
ESM::LandTexture::load(esm);
int plugin = esm.getIndex();
std::ostringstream stream;
stream << mIndex << "_" << plugin;
mId = stream.str();
}
}

@ -0,0 +1,22 @@
#ifndef CSM_WORLD_LANDTEXTURE_H
#define CSM_WORLD_LANDTEXTURE_H
#include <string>
#include <components/esm/loadltex.hpp>
namespace CSMWorld
{
/// \brief Wrapper for LandTexture record. Encodes mIndex and the plugin index (obtained from ESMReader)
/// in the ID.
///
/// \attention The mId field of the ESM::LandTexture struct is not used.
struct LandTexture : public ESM::LandTexture
{
std::string mId;
void load (ESM::ESMReader &esm);
};
}
#endif

@ -10,6 +10,9 @@
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "elements.hpp"
#include "terrainstorage.hpp"
bool CSVRender::Cell::removeObject (const std::string& id) bool CSVRender::Cell::removeObject (const std::string& id)
{ {
std::map<std::string, Object *>::iterator iter = std::map<std::string, Object *>::iterator iter =
@ -67,6 +70,18 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
int rows = references.rowCount(); int rows = references.rowCount();
addObjects (0, rows-1); addObjects (0, rows-1);
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
int landIndex = land.searchId(mId);
if (landIndex != -1)
{
mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true,
Terrain::Align_XY));
const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get();
mTerrain->loadCell(esmLand->mX,
esmLand->mY);
}
} }
CSVRender::Cell::~Cell() CSVRender::Cell::~Cell()
@ -198,4 +213,4 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
return false; return false;
return addObjects (start, end); return addObjects (start, end);
} }

@ -6,6 +6,8 @@
#include <OgreVector3.h> #include <OgreVector3.h>
#include <components/terrain/terraingrid.hpp>
#include "object.hpp" #include "object.hpp"
class QModelIndex; class QModelIndex;
@ -29,6 +31,7 @@ namespace CSVRender
std::string mId; std::string mId;
Ogre::SceneNode *mCellNode; Ogre::SceneNode *mCellNode;
std::map<std::string, Object *> mObjects; std::map<std::string, Object *> mObjects;
std::auto_ptr<Terrain::TerrainGrid> mTerrain;
/// Ignored if cell does not have an object with the given ID. /// Ignored if cell does not have an object with the given ID.
/// ///

@ -110,7 +110,7 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
{ {
int index = cells.searchId (iter->getId (mWorldspace)); int index = cells.searchId (iter->getId (mWorldspace));
if (index!=0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted && if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted &&
mCells.find (*iter)==mCells.end()) mCells.find (*iter)==mCells.end())
{ {
if (setCamera) if (setCamera)

@ -0,0 +1,43 @@
#include "terrainstorage.hpp"
namespace CSVRender
{
TerrainStorage::TerrainStorage(const CSMWorld::Data &data)
: mData(data)
{
}
ESM::Land* TerrainStorage::getLand(int cellX, int cellY)
{
std::ostringstream stream;
stream << "#" << cellX << " " << cellY;
// The cell isn't guaranteed to have Land. This is because the terrain implementation
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
int index = mData.getLand().searchId(stream.str());
if (index == -1)
return NULL;
ESM::Land* land = mData.getLand().getRecord(index).get().mLand.get();
int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(mask))
land->loadData(mask);
return land;
}
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
{
std::ostringstream stream;
stream << index << "_" << plugin;
return &mData.getLandTextures().getRecord(stream.str()).get();
}
void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)
{
// not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells
throw std::runtime_error("getBounds not implemented");
}
}

@ -0,0 +1,29 @@
#ifndef OPENCS_RENDER_TERRAINSTORAGE_H
#define OPENCS_RENDER_TERRAINSTORAGE_H
#include <components/esmterrain/storage.hpp>
#include "../../model/world/data.hpp"
namespace CSVRender
{
/**
* @brief A bridge between the terrain component and OpenCS's terrain data storage.
*/
class TerrainStorage : public ESMTerrain::Storage
{
public:
TerrainStorage(const CSMWorld::Data& data);
private:
const CSMWorld::Data& mData;
virtual ESM::Land* getLand (int cellX, int cellY);
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
};
}
#endif

@ -478,7 +478,7 @@ void OMW::Engine::go()
} }
// Start the main rendering loop // Start the main rendering loop
while (!mEnvironment.get().getStateManager()->hasQuitRequest()) while (!MWBase::Environment::get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame(); Ogre::Root::getSingleton().renderOneFrame();
// Save user settings // Save user settings

@ -541,6 +541,8 @@ namespace MWBase
/// Resets all actors in the current active cells to their original location within that cell. /// Resets all actors in the current active cells to their original location within that cell.
virtual void resetActors() = 0; virtual void resetActors() = 0;
virtual bool isWalkingOnWater (const MWWorld::Ptr& actor) = 0;
}; };
} }

@ -230,7 +230,7 @@ namespace MWClass
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = stats.getFatigue(); MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon.isEmpty()) if (!weapon.isEmpty())
fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult; fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult;
@ -537,7 +537,7 @@ namespace MWClass
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
@ -738,7 +738,7 @@ namespace MWClass
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos)) if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return 2; return 2;
if(world->isOnGround(ptr)) if(world->isOnGround(ptr))
return 0; return 0;
@ -748,7 +748,7 @@ namespace MWClass
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos)) if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return 3; return 3;
if(world->isOnGround(ptr)) if(world->isOnGround(ptr))
return 1; return 1;

@ -302,11 +302,11 @@ namespace MWClass
Misc::StringUtils::toLower(faction); Misc::StringUtils::toLower(faction);
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{ {
data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt52.mRank; data->mNpcStats.setFactionRank(faction, (int)ref->mBase->mNpdt52.mRank);
} }
else else
{ {
data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt12.mRank; data->mNpcStats.setFactionRank(faction, (int)ref->mBase->mNpdt12.mRank);
} }
} }
@ -495,7 +495,7 @@ namespace MWClass
const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat(); const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat(); const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue(); MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon.isEmpty()) if (!weapon.isEmpty())
fatigueLoss += weapon.getClass().getWeight(weapon) * getNpcStats(ptr).getAttackStrength() * fWeaponFatigueMult; fatigueLoss += weapon.getClass().getWeight(weapon) * getNpcStats(ptr).getAttackStrength() * fWeaponFatigueMult;
@ -911,7 +911,7 @@ namespace MWClass
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
@ -927,7 +927,7 @@ namespace MWClass
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(getEncumbrance(ptr) > getCapacity(ptr))
moveSpeed = 0.0f; moveSpeed = 0.0f;
else if(mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 && else if(mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 &&
world->isLevitationEnabled()) world->isLevitationEnabled())
@ -1222,7 +1222,7 @@ namespace MWClass
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isSwimming(ptr)) if(world->isSwimming(ptr))
return "Swim Left"; return "Swim Left";
if(world->isUnderwater(ptr.getCell(), pos)) if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return "FootWaterLeft"; return "FootWaterLeft";
if(world->isOnGround(ptr)) if(world->isOnGround(ptr))
{ {
@ -1249,7 +1249,7 @@ namespace MWClass
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isSwimming(ptr)) if(world->isSwimming(ptr))
return "Swim Right"; return "Swim Right";
if(world->isUnderwater(ptr.getCell(), pos)) if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return "FootWaterRight"; return "FootWaterRight";
if(world->isOnGround(ptr)) if(world->isOnGround(ptr))
{ {
@ -1274,7 +1274,7 @@ namespace MWClass
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos)) if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return "DefaultLandWater"; return "DefaultLandWater";
if(world->isOnGround(ptr)) if(world->isOnGround(ptr))
return "Body Fall Medium"; return "Body Fall Medium";

@ -63,7 +63,7 @@ namespace MWClass
virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const;
///< Return item max health or throw an exception, if class does not have item health ///< Return item max health or throw an exception, if class does not have item health
/// (default implementation: throw an exceoption) /// (default implementation: throw an exception)
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;

@ -68,7 +68,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
return false; return false;
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor); MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
std::map<std::string, int>::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction)); std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
if (iter==stats.getFactionRanks().end()) if (iter==stats.getFactionRanks().end())
return false; return false;
@ -112,7 +112,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
if (!info.mPcFaction.empty()) if (!info.mPcFaction.empty())
{ {
MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player); MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player);
std::map<std::string,int>::iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction)); std::map<std::string,int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction));
if(iter==stats.getFactionRanks().end()) if(iter==stats.getFactionRanks().end())
return false; return false;
@ -379,7 +379,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return 0; return 0;
std::pair<std::string, int> faction = const std::pair<std::string, int> faction =
*mActor.getClass().getNpcStats (mActor).getFactionRanks().begin(); *mActor.getClass().getNpcStats (mActor).getFactionRanks().begin();
int rank = getFactionRank (player, faction.first); int rank = getFactionRank (player, faction.first);

@ -21,7 +21,7 @@ namespace MWGui
{ {
/* BookTextParser */ /* BookTextParser */
BookTextParser::BookTextParser(const std::string & text) BookTextParser::BookTextParser(const std::string & text)
: mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true) : mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true), mClosingTag(false)
{ {
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
mText = Interpreter::fixDefinesBook(mText, interpreterContext); mText = Interpreter::fixDefinesBook(mText, interpreterContext);
@ -40,7 +40,7 @@ namespace MWGui
mTagTypes[tag] = type; mTagTypes[tag] = type;
} }
std::string BookTextParser::getReadyText() std::string BookTextParser::getReadyText() const
{ {
return mReadyText; return mReadyText;
} }
@ -81,7 +81,6 @@ namespace MWGui
if (type == Event_ImgTag) if (type == Event_ImgTag)
{ {
mIgnoreLineEndings = false;
mIgnoreNewlineTags = false; mIgnoreNewlineTags = false;
} }
@ -117,12 +116,27 @@ namespace MWGui
return mAttributes; return mAttributes;
} }
bool BookTextParser::isClosingTag() const
{
return mClosingTag;
}
void BookTextParser::parseTag(std::string tag) void BookTextParser::parseTag(std::string tag)
{ {
size_t tagNameEndPos = tag.find(' '); size_t tagNameEndPos = tag.find(' ');
mAttributes.clear();
mTag = tag.substr(0, tagNameEndPos); mTag = tag.substr(0, tagNameEndPos);
Misc::StringUtils::toLower(mTag); Misc::StringUtils::toLower(mTag);
mAttributes.clear(); if (mTag.empty())
return;
mClosingTag = (mTag[0] == '/');
if (mClosingTag)
{
mTag.erase(mTag.begin());
return;
}
if (tagNameEndPos == std::string::npos) if (tagNameEndPos == std::string::npos)
return; return;
tag.erase(0, tagNameEndPos+1); tag.erase(0, tagNameEndPos+1);
@ -183,6 +197,9 @@ namespace MWGui
paper->setNeedMouseFocus(false); paper->setNeedMouseFocus(false);
BookTextParser parser(markup); BookTextParser parser(markup);
bool brBeforeLastTag = false;
bool isPrevImg = false;
for (;;) for (;;)
{ {
BookTextParser::Events event = parser.next(); BookTextParser::Events event = parser.next();
@ -190,10 +207,20 @@ namespace MWGui
continue; continue;
std::string plainText = parser.getReadyText(); std::string plainText = parser.getReadyText();
if (!plainText.empty()) if (plainText.empty())
brBeforeLastTag = false;
else
{ {
// if there's a newline at the end of the box caption, remove it // Each block of text (between two tags / boundary and tag) will be displayed in a separate editbox widget,
if (plainText[plainText.size()-1] == '\n') // which means an additional linebreak will be created between them.
// ^ This is not what vanilla MW assumes, so we must deal with line breaks around tags appropriately.
bool brAtStart = (plainText[0] == '\n');
bool brAtEnd = (plainText[plainText.size()-1] == '\n');
if (brAtStart && !brBeforeLastTag && !isPrevImg)
plainText.erase(plainText.begin());
if (plainText.size() && brAtEnd)
plainText.erase(plainText.end()-1); plainText.erase(plainText.end()-1);
#if (MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 2, 2)) #if (MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 2, 2))
@ -206,13 +233,21 @@ namespace MWGui
} }
#endif #endif
TextElement elem(paper, pag, mTextStyle, plainText); if (!plainText.empty() || brBeforeLastTag || isPrevImg)
elem.paginate(); {
TextElement elem(paper, pag, mBlockStyle,
mTextStyle, plainText);
elem.paginate();
}
brBeforeLastTag = brAtEnd;
} }
if (event == BookTextParser::Event_EOF) if (event == BookTextParser::Event_EOF)
break; break;
isPrevImg = (event == BookTextParser::Event_ImgTag);
switch (event) switch (event)
{ {
case BookTextParser::Event_ImgTag: case BookTextParser::Event_ImgTag:
@ -226,12 +261,16 @@ namespace MWGui
int width = boost::lexical_cast<int>(attr.at("width")); int width = boost::lexical_cast<int>(attr.at("width"));
int height = boost::lexical_cast<int>(attr.at("height")); int height = boost::lexical_cast<int>(attr.at("height"));
ImageElement elem(paper, pag, src, width, height); ImageElement elem(paper, pag, mBlockStyle,
src, width, height);
elem.paginate(); elem.paginate();
break; break;
} }
case BookTextParser::Event_FontTag: case BookTextParser::Event_FontTag:
handleFont(parser.getAttributes()); if (parser.isClosingTag())
resetFontProperties();
else
handleFont(parser.getAttributes());
break; break;
case BookTextParser::Event_DivTag: case BookTextParser::Event_DivTag:
handleDiv(parser.getAttributes()); handleDiv(parser.getAttributes());
@ -255,6 +294,11 @@ namespace MWGui
return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight()); return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight());
} }
void BookFormatter::resetFontProperties()
{
mTextStyle = TextStyle();
}
void BookFormatter::handleDiv(const BookTextParser::Attributes & attr) void BookFormatter::handleDiv(const BookTextParser::Attributes & attr)
{ {
if (attr.find("align") == attr.end()) if (attr.find("align") == attr.end())
@ -263,9 +307,11 @@ namespace MWGui
std::string align = attr.at("align"); std::string align = attr.at("align");
if (Misc::StringUtils::ciEqual(align, "center")) if (Misc::StringUtils::ciEqual(align, "center"))
mTextStyle.mTextAlign = MyGUI::Align::HCenter; mBlockStyle.mAlign = MyGUI::Align::HCenter;
else if (Misc::StringUtils::ciEqual(align, "left")) else if (Misc::StringUtils::ciEqual(align, "left"))
mTextStyle.mTextAlign = MyGUI::Align::Left; mBlockStyle.mAlign = MyGUI::Align::Left;
else if (Misc::StringUtils::ciEqual(align, "right"))
mBlockStyle.mAlign = MyGUI::Align::Right;
} }
void BookFormatter::handleFont(const BookTextParser::Attributes & attr) void BookFormatter::handleFont(const BookTextParser::Attributes & attr)
@ -296,8 +342,8 @@ namespace MWGui
} }
/* GraphicElement */ /* GraphicElement */
GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag) GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle)
: mParent(parent), mPaginator(pag) : mParent(parent), mPaginator(pag), mBlockStyle(blockStyle)
{ {
} }
@ -320,10 +366,10 @@ namespace MWGui
} }
/* TextElement */ /* TextElement */
TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
const TextStyle & style, const std::string & text) const TextStyle & textStyle, const std::string & text)
: GraphicElement(parent, pag), : GraphicElement(parent, pag, blockStyle),
mStyle(style) mTextStyle(textStyle)
{ {
MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText", MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText",
MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,
@ -333,9 +379,9 @@ namespace MWGui
box->setProperty("WordWrap", "true"); box->setProperty("WordWrap", "true");
box->setProperty("NeedMouse", "false"); box->setProperty("NeedMouse", "false");
box->setMaxTextLength(text.size()); box->setMaxTextLength(text.size());
box->setTextAlign(mStyle.mTextAlign); box->setTextAlign(mBlockStyle.mAlign);
box->setTextColour(mStyle.mColour); box->setTextColour(mTextStyle.mColour);
box->setFontName(mStyle.mFont); box->setFontName(mTextStyle.mFont);
box->setCaption(MyGUI::TextIterator::toTagsString(text)); box->setCaption(MyGUI::TextIterator::toTagsString(text));
box->setSize(box->getSize().width, box->getTextSize().height); box->setSize(box->getSize().width, box->getTextSize().height);
mEditBox = box; mEditBox = box;
@ -343,7 +389,7 @@ namespace MWGui
int TextElement::currentFontHeight() const int TextElement::currentFontHeight() const
{ {
std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont);
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
} }
@ -375,13 +421,21 @@ namespace MWGui
} }
/* ImageElement */ /* ImageElement */
ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
const std::string & src, int width, int height) const std::string & src, int width, int height)
: GraphicElement(parent, pag), : GraphicElement(parent, pag, blockStyle),
mImageHeight(height) mImageHeight(height)
{ {
int left = 0;
if (mBlockStyle.mAlign.isHCenter())
left += (pag.getPageWidth() - width) / 2;
else if (mBlockStyle.mAlign.isLeft())
left = 0;
else if (mBlockStyle.mAlign.isRight())
left += pag.getPageWidth() - width;
mImageBox = parent->createWidget<MyGUI::ImageBox> ("ImageBox", mImageBox = parent->createWidget<MyGUI::ImageBox> ("ImageBox",
MyGUI::IntCoord(0, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top, MyGUI::IntCoord(left, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top,
parent->getName() + boost::lexical_cast<std::string>(parent->getChildCount())); parent->getName() + boost::lexical_cast<std::string>(parent->getChildCount()));
std::string image = Misc::ResourceHelpers::correctBookartPath(src, width, mImageHeight); std::string image = Misc::ResourceHelpers::correctBookartPath(src, width, mImageHeight);

@ -14,14 +14,22 @@ namespace MWGui
mColour(0,0,0) mColour(0,0,0)
, mFont("Default") , mFont("Default")
, mTextSize(16) , mTextSize(16)
, mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top)
{ {
} }
MyGUI::Colour mColour; MyGUI::Colour mColour;
std::string mFont; std::string mFont;
int mTextSize; int mTextSize;
MyGUI::Align mTextAlign; };
struct BlockStyle
{
BlockStyle() :
mAlign(MyGUI::Align::Left | MyGUI::Align::Top)
{
}
MyGUI::Align mAlign;
}; };
class BookTextParser class BookTextParser
@ -40,15 +48,18 @@ namespace MWGui
}; };
BookTextParser(const std::string & text); BookTextParser(const std::string & text);
void registerTag(const std::string & tag, Events type);
std::string getReadyText();
Events next(); Events next();
void flushBuffer();
const Attributes & getAttributes() const; const Attributes & getAttributes() const;
void parseTag(std::string tag); std::string getReadyText() const;
bool isClosingTag() const;
private: private:
void registerTag(const std::string & tag, Events type);
void flushBuffer();
void parseTag(std::string tag);
size_t mIndex; size_t mIndex;
std::string mText; std::string mText;
std::string mReadyText; std::string mReadyText;
@ -57,6 +68,7 @@ namespace MWGui
bool mIgnoreLineEndings; bool mIgnoreLineEndings;
Attributes mAttributes; Attributes mAttributes;
std::string mTag; std::string mTag;
bool mClosingTag;
std::map<std::string, Events> mTagTypes; std::map<std::string, Events> mTagTypes;
std::string mBuffer; std::string mBuffer;
}; };
@ -101,18 +113,20 @@ namespace MWGui
Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight); Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight);
Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup); Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup);
protected: private:
void handleImg(const BookTextParser::Attributes & attr); void resetFontProperties();
void handleDiv(const BookTextParser::Attributes & attr); void handleDiv(const BookTextParser::Attributes & attr);
void handleFont(const BookTextParser::Attributes & attr); void handleFont(const BookTextParser::Attributes & attr);
private:
TextStyle mTextStyle; TextStyle mTextStyle;
BlockStyle mBlockStyle;
}; };
class GraphicElement class GraphicElement
{ {
public: public:
GraphicElement(MyGUI::Widget * parent, Paginator & pag); GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle);
virtual int getHeight() = 0; virtual int getHeight() = 0;
virtual void paginate(); virtual void paginate();
virtual int pageSplit(); virtual int pageSplit();
@ -120,24 +134,27 @@ namespace MWGui
protected: protected:
MyGUI::Widget * mParent; MyGUI::Widget * mParent;
Paginator & mPaginator; Paginator & mPaginator;
BlockStyle mBlockStyle;
}; };
class TextElement : public GraphicElement class TextElement : public GraphicElement
{ {
public: public:
TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text); TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
const TextStyle & textStyle, const std::string & text);
virtual int getHeight(); virtual int getHeight();
virtual int pageSplit(); virtual int pageSplit();
private: private:
int currentFontHeight() const; int currentFontHeight() const;
TextStyle mStyle; TextStyle mTextStyle;
MyGUI::EditBox * mEditBox; MyGUI::EditBox * mEditBox;
}; };
class ImageElement : public GraphicElement class ImageElement : public GraphicElement
{ {
public: public:
ImageElement(MyGUI::Widget * parent, Paginator & pag, const std::string & src, int width, int height); ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
const std::string & src, int width, int height);
virtual int getHeight(); virtual int getHeight();
virtual int pageSplit(); virtual int pageSplit();

@ -333,7 +333,8 @@ namespace MWGui
float e1 = 0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(); float e1 = 0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified();
float f1 = 0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(); float f1 = 0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified();
float pcTerm = (clampedDisposition - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm(); float dispositionTerm = gmst.find("fDispositionMod")->getFloat() * (clampedDisposition - 50);
float pcTerm = (dispositionTerm - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm();
float npcTerm = (d1 + e1 + f1) * sellerStats.getFatigueTerm(); float npcTerm = (d1 + e1 + f1) * sellerStats.getFatigueTerm();
float x = gmst.find("fBargainOfferMulti")->getFloat() * d + gmst.find("fBargainOfferBase")->getFloat(); float x = gmst.find("fBargainOfferMulti")->getFloat() * d + gmst.find("fBargainOfferBase")->getFloat();
if (buying) if (buying)

@ -385,7 +385,7 @@ namespace MWInput
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
mOverencumberedMessageDelay -= dt; mOverencumberedMessageDelay -= dt;
if (player.getClass().getEncumbrance(player) >= player.getClass().getCapacity(player)) if (player.getClass().getEncumbrance(player) > player.getClass().getCapacity(player))
{ {
mPlayer->setAutoMove (false); mPlayer->setAutoMove (false);
if (mOverencumberedMessageDelay <= 0) if (mOverencumberedMessageDelay <= 0)

@ -445,9 +445,7 @@ namespace MWMechanics
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float capacity = ptr.getClass().getCapacity(ptr); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
float encumbrance = ptr.getClass().getEncumbrance(ptr);
float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity);
if (normalizedEncumbrance > 1) if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1; normalizedEncumbrance = 1;
@ -1447,6 +1445,8 @@ namespace MWMechanics
continue; continue;
if (followTarget == actor) if (followTarget == actor)
list.push_back(iter->first); list.push_back(iter->first);
else
break;
} }
else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat)
break; break;

@ -148,7 +148,8 @@ bool AiSequence::isPackageDone() const
void AiSequence::execute (const MWWorld::Ptr& actor,float duration) void AiSequence::execute (const MWWorld::Ptr& actor,float duration)
{ {
if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr()) if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr()
&& !actor.getClass().getCreatureStats(actor).getKnockedDown())
{ {
if (!mPackages.empty()) if (!mPackages.empty())
{ {

@ -1428,7 +1428,9 @@ void CharacterController::update(float duration)
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat(); const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat();
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat(); const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat();
const float normalizedEncumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr); float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult; const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult;
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue(); DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);

@ -124,7 +124,7 @@ namespace MWMechanics
const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat();
const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = blockerStats.getFatigue(); MWMechanics::DynamicStat<float> fatigue = blockerStats.getFatigue();
float normalizedEncumbrance = blocker.getClass().getEncumbrance(blocker) / blocker.getClass().getCapacity(blocker); float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker);
normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); normalizedEncumbrance = std::min(1.f, normalizedEncumbrance);
float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult; float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult;
fatigueLoss += weapon.getClass().getWeight(weapon) * attackerStats.getAttackStrength() * fWeaponFatigueBlockMult; fatigueLoss += weapon.getClass().getWeight(weapon) * attackerStats.getAttackStrength() * fWeaponFatigueBlockMult;

@ -69,9 +69,41 @@ const std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() const
return mFactionRank; return mFactionRank;
} }
std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() void MWMechanics::NpcStats::raiseRank(const std::string &faction)
{ {
return mFactionRank; const std::string lower = Misc::StringUtils::lowerCase(faction);
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
if (it != mFactionRank.end())
{
// Does the next rank exist?
const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(lower);
if (it->second+1 < 10 && !faction->mRanks[it->second+1].empty())
it->second += 1;
}
}
void MWMechanics::NpcStats::lowerRank(const std::string &faction)
{
const std::string lower = Misc::StringUtils::lowerCase(faction);
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
if (it != mFactionRank.end())
{
it->second = std::max(0, it->second-1);
}
}
void MWMechanics::NpcStats::setFactionRank(const std::string &faction, int rank)
{
const std::string lower = Misc::StringUtils::lowerCase(faction);
mFactionRank[lower] = rank;
}
void MWMechanics::NpcStats::joinFaction(const std::string& faction)
{
const std::string lower = Misc::StringUtils::lowerCase(faction);
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
if (it == mFactionRank.end())
mFactionRank[lower] = 0;
} }
bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const

@ -70,7 +70,15 @@ namespace MWMechanics
SkillValue& getSkill (int index); SkillValue& getSkill (int index);
const std::map<std::string, int>& getFactionRanks() const; const std::map<std::string, int>& getFactionRanks() const;
std::map<std::string, int>& getFactionRanks(); /// Increase the rank in this faction by 1, if such a rank exists.
void raiseRank(const std::string& faction);
/// Lower the rank in this faction by 1, if such a rank exists.
void lowerRank(const std::string& faction);
/// Join this faction, setting the initial rank to 0.
void joinFaction(const std::string& faction);
/// Warning: this function performs no check whether the rank exists,
/// and should be used in initial actor setup only.
void setFactionRank(const std::string& faction, int rank);
const std::set<std::string>& getExpelled() const { return mExpelled; } const std::set<std::string>& getExpelled() const { return mExpelled; }
bool getExpelled(const std::string& factionID) const; bool getExpelled(const std::string& factionID) const;

@ -246,10 +246,7 @@ namespace MWMechanics
const ESM::Spell* spell, const MagicEffects* effects) const ESM::Spell* spell, const MagicEffects* effects)
{ {
float resistance = getEffectResistance(effectId, actor, caster, spell, effects); float resistance = getEffectResistance(effectId, actor, caster, spell, effects);
if (resistance >= 0) return 1 - resistance / 100.f;
return 1 - resistance / 100.f;
else
return -(resistance-100) / 100.f;
} }
/// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure. /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure.
@ -774,7 +771,7 @@ namespace MWMechanics
static const float fFatigueSpellBase = store.get<ESM::GameSetting>().find("fFatigueSpellBase")->getFloat(); static const float fFatigueSpellBase = store.get<ESM::GameSetting>().find("fFatigueSpellBase")->getFloat();
static const float fFatigueSpellMult = store.get<ESM::GameSetting>().find("fFatigueSpellMult")->getFloat(); static const float fFatigueSpellMult = store.get<ESM::GameSetting>().find("fFatigueSpellMult")->getFloat();
DynamicStat<float> fatigue = stats.getFatigue(); DynamicStat<float> fatigue = stats.getFatigue();
const float normalizedEncumbrance = mCaster.getClass().getEncumbrance(mCaster) / mCaster.getClass().getCapacity(mCaster); const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);
float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult); float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue); fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue);

@ -51,6 +51,10 @@ namespace MWMechanics
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
/// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional).
/// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness)
/// @param effects Override the actor's current magicEffects. Useful if there are effects currently
/// being applied (but not applied yet) that should also be considered.
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);

@ -728,7 +728,7 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co
{ {
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx; MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx;
if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0)
type = MWBase::SoundManager::Play_TypeFoot; type = MWBase::SoundManager::Play_TypeFoot;
sndMgr->playSound3D(mPtr, sound, volume, pitch, type); sndMgr->playSound3D(mPtr, sound, volume, pitch, type);
} }

@ -119,11 +119,14 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
// Set default texture filtering options // Set default texture filtering options
TextureFilterOptions tfo; TextureFilterOptions tfo;
std::string filter = Settings::Manager::getString("texture filtering", "General"); std::string filter = Settings::Manager::getString("texture filtering", "General");
#ifndef ANDROID
if (filter == "anisotropic") tfo = TFO_ANISOTROPIC; if (filter == "anisotropic") tfo = TFO_ANISOTROPIC;
else if (filter == "trilinear") tfo = TFO_TRILINEAR; else if (filter == "trilinear") tfo = TFO_TRILINEAR;
else if (filter == "bilinear") tfo = TFO_BILINEAR; else if (filter == "bilinear") tfo = TFO_BILINEAR;
else /*if (filter == "none")*/ tfo = TFO_NONE; else /*if (filter == "none")*/ tfo = TFO_NONE;
#else
tfo = TFO_NONE;
#endif
MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 );
@ -250,7 +253,6 @@ void RenderingManager::cellAdded (MWWorld::CellStore *store)
mObjects->buildStaticGeometry (*store); mObjects->buildStaticGeometry (*store);
sh::Factory::getInstance().unloadUnreferencedMaterials(); sh::Factory::getInstance().unloadUnreferencedMaterials();
mDebugging->cellAdded(store); mDebugging->cellAdded(store);
waterAdded(store);
} }
void RenderingManager::addObject (const MWWorld::Ptr& ptr){ void RenderingManager::addObject (const MWWorld::Ptr& ptr){
@ -421,18 +423,12 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt)
mOcclusionQuery->setActive(false); mOcclusionQuery->setActive(false);
} }
void RenderingManager::waterAdded (MWWorld::CellStore *store) void RenderingManager::setWaterEnabled(bool enable)
{ {
if (store->getCell()->mData.mFlags & ESM::Cell::HasWater) mWater->setActive(enable);
{
mWater->changeCell (store->getCell());
mWater->setActive(true);
}
else
removeWater();
} }
void RenderingManager::setWaterHeight(const float height) void RenderingManager::setWaterHeight(float height)
{ {
mWater->setHeight(height); mWater->setHeight(height);
} }

@ -100,7 +100,6 @@ public:
/// \todo this function should be removed later. Instead the rendering subsystems should track /// \todo this function should be removed later. Instead the rendering subsystems should track
/// when rebatching is needed and update automatically at the end of each frame. /// when rebatching is needed and update automatically at the end of each frame.
void cellAdded (MWWorld::CellStore *store); void cellAdded (MWWorld::CellStore *store);
void waterAdded(MWWorld::CellStore *store);
/// Clear all savegame-specific data (i.e. fog of war textures) /// Clear all savegame-specific data (i.e. fog of war textures)
void clear(); void clear();
@ -121,7 +120,8 @@ public:
/// Updates an object's rotation /// Updates an object's rotation
void rotateObject (const MWWorld::Ptr& ptr); void rotateObject (const MWWorld::Ptr& ptr);
void setWaterHeight(const float height); void setWaterHeight(float height);
void setWaterEnabled(bool enabled);
bool toggleWater(); bool toggleWater();
bool toggleWorld(); bool toggleWorld();

@ -81,7 +81,7 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor)
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::CreatureStats& attackerStats = actor.getClass().getCreatureStats(actor); MWMechanics::CreatureStats& attackerStats = actor.getClass().getCreatureStats(actor);
MWMechanics::DynamicStat<float> fatigue = attackerStats.getFatigue(); MWMechanics::DynamicStat<float> fatigue = attackerStats.getFatigue();
const float normalizedEncumbrance = actor.getClass().getEncumbrance(actor) / actor.getClass().getCapacity(actor); const float normalizedEncumbrance = actor.getClass().getNormalizedEncumbrance(actor);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon->isEmpty()) if (!weapon->isEmpty())
fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult; fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult;

@ -290,18 +290,15 @@ namespace MWScript
const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); const std::string &name = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
int count = 0;
MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr);
for (MWWorld::ContainerStoreIterator it = invStore.begin(MWWorld::ContainerStore::Type_Miscellaneous); for (MWWorld::ContainerStoreIterator it = invStore.begin(MWWorld::ContainerStore::Type_Miscellaneous);
it != invStore.end(); ++it) it != invStore.end(); ++it)
{ {
if (::Misc::StringUtils::ciEqual(it->getCellRef().getSoul(), name)) if (::Misc::StringUtils::ciEqual(it->getCellRef().getSoul(), name))
{ ++count;
runtime.push(1);
return;
}
} }
runtime.push(0); runtime.push(count);
} }
}; };

@ -310,7 +310,7 @@ namespace MWScript
std::string InterpreterContext::getNPCRank() const std::string InterpreterContext::getNPCRank() const
{ {
std::map<std::string, int> ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks(); const std::map<std::string, int>& ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.begin(); std::map<std::string, int>::const_iterator it = ranks.begin();
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
@ -348,7 +348,7 @@ namespace MWScript
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first; std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks(); const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId); std::map<std::string, int>::const_iterator it = ranks.find(factionId);
int rank = -1; int rank = -1;
if (it != ranks.end()) if (it != ranks.end())
@ -375,7 +375,7 @@ namespace MWScript
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first; std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks(); const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId); std::map<std::string, int>::const_iterator it = ranks.find(factionId);
int rank = -1; int rank = -1;
if (it != ranks.end()) if (it != ranks.end())

@ -548,10 +548,7 @@ namespace MWScript
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end()) player.getClass().getNpcStats(player).joinFaction(factionID);
{
player.getClass().getNpcStats(player).getFactionRanks()[factionID] = 0;
}
} }
} }
}; };
@ -585,13 +582,11 @@ namespace MWScript
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end()) if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end())
{ {
player.getClass().getNpcStats(player).getFactionRanks()[factionID] = 0; player.getClass().getNpcStats(player).joinFaction(factionID);
} }
else else
{ {
player.getClass().getNpcStats(player).getFactionRanks()[factionID] = player.getClass().getNpcStats(player).raiseRank(factionID);
std::min(player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1,
9);
} }
} }
} }
@ -624,11 +619,7 @@ namespace MWScript
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end()) player.getClass().getNpcStats(player).lowerRank(factionID);
{
player.getClass().getNpcStats(player).getFactionRanks()[factionID] =
std::max(0, player.getClass().getNpcStats(player).getFactionRanks()[factionID]-1);
}
} }
} }
}; };
@ -668,7 +659,7 @@ namespace MWScript
{ {
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end()) if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end())
{ {
runtime.push(player.getClass().getNpcStats(player).getFactionRanks()[factionID]); runtime.push(player.getClass().getNpcStats(player).getFactionRanks().at(factionID));
} }
else else
{ {
@ -1036,8 +1027,7 @@ namespace MWScript
if (ptr == player) if (ptr == player)
return; return;
std::map<std::string, int>& ranks = ptr.getClass().getNpcStats(ptr).getFactionRanks (); ptr.getClass().getNpcStats(ptr).raiseRank(factionID);
ranks[factionID] = std::min(9, ranks[factionID]+1);
} }
}; };
@ -1063,8 +1053,7 @@ namespace MWScript
if (ptr == player) if (ptr == player)
return; return;
std::map<std::string, int>& ranks = ptr.getClass().getNpcStats(ptr).getFactionRanks (); ptr.getClass().getNpcStats(ptr).lowerRank(factionID);
ranks[factionID] = std::max(0, ranks[factionID]-1);
} }
}; };

@ -5,6 +5,23 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "player.hpp" #include "player.hpp"
namespace
{
void getFollowers (const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out)
{
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
{
if (out.insert(*it).second)
{
getFollowers(*it, out);
}
}
}
}
namespace MWWorld namespace MWWorld
{ {
ActionTeleport::ActionTeleport (const std::string& cellName, ActionTeleport::ActionTeleport (const std::string& cellName,
@ -16,8 +33,9 @@ namespace MWWorld
void ActionTeleport::executeImp (const Ptr& actor) void ActionTeleport::executeImp (const Ptr& actor)
{ {
//find any NPC that is following the actor and teleport him too //find any NPC that is following the actor and teleport him too
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); std::set<MWWorld::Ptr> followers;
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it) getFollowers(actor, followers);
for(std::set<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
{ {
teleport(*it); teleport(*it);
} }

@ -420,4 +420,13 @@ namespace MWWorld
{ {
throw std::runtime_error("this is not a door"); throw std::runtime_error("this is not a door");
} }
float Class::getNormalizedEncumbrance(const Ptr &ptr) const
{
float capacity = getCapacity(ptr);
if (capacity == 0)
return 1.f;
return getEncumbrance(ptr) / capacity;
}
} }

@ -96,7 +96,7 @@ namespace MWWorld
virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const; virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const;
///< Return creature stats or throw an exception, if class does not have creature stats ///< Return creature stats or throw an exception, if class does not have creature stats
/// (default implementation: throw an exceoption) /// (default implementation: throw an exception)
virtual bool hasToolTip (const Ptr& ptr) const; virtual bool hasToolTip (const Ptr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @return true if this object has a tooltip when focused (default implementation: false)
@ -106,7 +106,7 @@ namespace MWWorld
virtual MWMechanics::NpcStats& getNpcStats (const Ptr& ptr) const; virtual MWMechanics::NpcStats& getNpcStats (const Ptr& ptr) const;
///< Return NPC stats or throw an exception, if class does not have NPC stats ///< Return NPC stats or throw an exception, if class does not have NPC stats
/// (default implementation: throw an exceoption) /// (default implementation: throw an exception)
virtual bool hasItemHealth (const Ptr& ptr) const; virtual bool hasItemHealth (const Ptr& ptr) const;
///< \return Item health data available? (default implementation: false) ///< \return Item health data available? (default implementation: false)
@ -123,7 +123,7 @@ namespace MWWorld
/// of the given attacker, and whoever is hit. /// of the given attacker, and whoever is hit.
/// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType /// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType
/// enums. ignored for creature attacks. /// enums. ignored for creature attacks.
/// (default implementation: throw an exceoption) /// (default implementation: throw an exception)
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is ///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
@ -139,7 +139,7 @@ namespace MWWorld
///< Sets a new current health value for the actor, optionally specifying the object causing ///< Sets a new current health value for the actor, optionally specifying the object causing
/// the change. Use this instead of using CreatureStats directly as this will make sure the /// the change. Use this instead of using CreatureStats directly as this will make sure the
/// correct dialog and actor states are properly handled when being hurt or healed. /// correct dialog and actor states are properly handled when being hurt or healed.
/// (default implementation: throw an exceoption) /// (default implementation: throw an exception)
virtual boost::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const; virtual boost::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;
///< Generate action for activation (default implementation: return a null action). ///< Generate action for activation (default implementation: return a null action).
@ -151,11 +151,11 @@ namespace MWWorld
virtual ContainerStore& getContainerStore (const Ptr& ptr) const; virtual ContainerStore& getContainerStore (const Ptr& ptr) const;
///< Return container store or throw an exception, if class does not have a ///< Return container store or throw an exception, if class does not have a
/// container store (default implementation: throw an exceoption) /// container store (default implementation: throw an exception)
virtual InventoryStore& getInventoryStore (const Ptr& ptr) const; virtual InventoryStore& getInventoryStore (const Ptr& ptr) const;
///< Return inventory store or throw an exception, if class does not have a ///< Return inventory store or throw an exception, if class does not have a
/// inventory store (default implementation: throw an exceoption) /// inventory store (default implementation: throw an exception)
virtual bool hasInventoryStore (const Ptr& ptr) const; virtual bool hasInventoryStore (const Ptr& ptr) const;
///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false) ///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false)
@ -223,6 +223,9 @@ namespace MWWorld
/// effects). Throws an exception, if the object can't hold other objects. /// effects). Throws an exception, if the object can't hold other objects.
/// (default implementation: throws an exception) /// (default implementation: throws an exception)
virtual float getNormalizedEncumbrance (const MWWorld::Ptr& ptr) const;
///< Returns encumbrance re-scaled to capacity
virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id, virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Apply \a id on \a ptr. ///< Apply \a id on \a ptr.

@ -68,6 +68,7 @@ MWWorld::InventoryStore::InventoryStore()
, mUpdatesEnabled (true) , mUpdatesEnabled (true)
, mFirstAutoEquip(true) , mFirstAutoEquip(true)
, mListener(NULL) , mListener(NULL)
, mRechargingItemsUpToDate(false)
{ {
initSlots (mSlots); initSlots (mSlots);
} }
@ -80,6 +81,7 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
, mListener(store.mListener) , mListener(store.mListener)
, mUpdatesEnabled(store.mUpdatesEnabled) , mUpdatesEnabled(store.mUpdatesEnabled)
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes) , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
, mRechargingItemsUpToDate(false)
{ {
copySlots (store); copySlots (store);
} }
@ -90,6 +92,7 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor
mMagicEffects = store.mMagicEffects; mMagicEffects = store.mMagicEffects;
mFirstAutoEquip = store.mFirstAutoEquip; mFirstAutoEquip = store.mFirstAutoEquip;
mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes;
mRechargingItemsUpToDate = false;
ContainerStore::operator= (store); ContainerStore::operator= (store);
mSlots.clear(); mSlots.clear();
copySlots (store); copySlots (store);
@ -110,8 +113,6 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr,
autoEquip(actorPtr); autoEquip(actorPtr);
} }
updateRechargingItems();
return retVal; return retVal;
} }
@ -485,8 +486,6 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor
mSelectedEnchantItem = end(); mSelectedEnchantItem = end();
} }
updateRechargingItems();
return retCount; return retCount;
} }
@ -606,6 +605,11 @@ void MWWorld::InventoryStore::updateRechargingItems()
void MWWorld::InventoryStore::rechargeItems(float duration) void MWWorld::InventoryStore::rechargeItems(float duration)
{ {
if (!mRechargingItemsUpToDate)
{
updateRechargingItems();
mRechargingItemsUpToDate = true;
}
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
{ {
if (it->first->getCellRef().getEnchantmentCharge() == -1 if (it->first->getCellRef().getEnchantmentCharge() == -1

@ -102,6 +102,8 @@ namespace MWWorld
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems; typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
TRechargingItems mRechargingItems; TRechargingItems mRechargingItems;
bool mRechargingItemsUpToDate;
void copySlots (const InventoryStore& store); void copySlots (const InventoryStore& store);
void initSlots (TSlots& slots_); void initSlots (TSlots& slots_);

@ -238,10 +238,31 @@ namespace MWWorld
physicActor->setOnGround(false); physicActor->setOnGround(false);
return position; return position;
} }
else
{
// Check if we actually found a valid spawn point (use an infinitely thin ray this time).
// Required for some broken door destinations in Morrowind.esm, where the spawn point
// intersects with other geometry if the actor's base is taken into account
btVector3 from = BtOgre::Convert::toBullet(position);
btVector3 to = from - btVector3(0,0,maxHeight);
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
resultCallback1.m_collisionFilterGroup = 0xff;
resultCallback1.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap;
engine->mDynamicsWorld->rayTest(from, to, resultCallback1);
if (resultCallback1.hasHit() &&
(BtOgre::Convert::toOgre(resultCallback1.m_hitPointWorld).distance(tracer.mEndPos) > 30
|| getSlope(tracer.mPlaneNormal) > sMaxSlope))
{
physicActor->setOnGround(getSlope(BtOgre::Convert::toOgre(resultCallback1.m_hitNormalWorld)) <= sMaxSlope);
return BtOgre::Convert::toOgre(resultCallback1.m_hitPointWorld) + Ogre::Vector3(0,0,1.f);
}
physicActor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope); physicActor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope);
return tracer.mEndPos; return tracer.mEndPos;
}
} }
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
@ -257,8 +278,10 @@ namespace MWWorld
if (!ptr.getClass().canWalk(ptr) && !ptr.getClass().canFly(ptr) && !ptr.getClass().canSwim(ptr)) if (!ptr.getClass().canWalk(ptr) && !ptr.getClass().canFly(ptr) && !ptr.getClass().canSwim(ptr))
return position; return position;
/* Anything to collide with? */
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
// Reset per-frame data
physicActor->setWalkingOnWater(false);
/* Anything to collide with? */
if(!physicActor || !physicActor->getCollisionMode()) if(!physicActor || !physicActor->getCollisionMode())
{ {
return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
@ -286,9 +309,6 @@ namespace MWWorld
*/ */
OEngine::Physic::ActorTracer tracer; OEngine::Physic::ActorTracer tracer;
bool isOnGround = physicActor->getOnGround();
if (movement.z > 0.f)
isOnGround = false;
Ogre::Vector3 inertia(0.0f); Ogre::Vector3 inertia(0.0f);
Ogre::Vector3 velocity; Ogre::Vector3 velocity;
@ -405,8 +425,6 @@ namespace MWWorld
if((ptr.getClass().canSwim(ptr) && !ptr.getClass().canWalk(ptr)) if((ptr.getClass().canSwim(ptr) && !ptr.getClass().canWalk(ptr))
&& newPosition.z > (waterlevel - halfExtents.z * 0.5)) && newPosition.z > (waterlevel - halfExtents.z * 0.5))
newPosition = oldPosition; newPosition = oldPosition;
else // Only on the ground if there's gravity
isOnGround = !(newPosition.z < waterlevel || isFlying);
} }
else else
{ {
@ -423,10 +441,11 @@ namespace MWWorld
} }
} }
if (!(inertia.z > 0.f) && !(newPosition.z < waterlevel || isFlying)) bool isOnGround = false;
if (!(inertia.z > 0.f) && !(newPosition.z < waterlevel))
{ {
Ogre::Vector3 from = newPosition; Ogre::Vector3 from = newPosition;
Ogre::Vector3 to = newPosition - (isOnGround ? Ogre::Vector3 to = newPosition - (physicActor->getOnGround() ?
Ogre::Vector3(0,0,sStepSize+2.f) : Ogre::Vector3(0,0,2.f)); Ogre::Vector3(0,0,sStepSize+2.f) : Ogre::Vector3(0,0,2.f));
tracer.doTrace(colobj, from, to, engine); tracer.doTrace(colobj, from, to, engine);
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
@ -436,8 +455,11 @@ namespace MWWorld
{ {
standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName; standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName;
} }
if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Water)
physicActor->setWalkingOnWater(true);
newPosition.z = tracer.mEndPos.z + 1.0f; if (!isFlying)
newPosition.z = tracer.mEndPos.z + 1.0f;
isOnGround = true; isOnGround = true;
} }
@ -464,7 +486,7 @@ namespace MWWorld
PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) :
mRender(_rend), mEngine(0), mTimeAccum(0.0f) mRender(_rend), mEngine(0), mTimeAccum(0.0f), mWaterEnabled(false), mWaterHeight(0)
{ {
// Create physics. shapeLoader is deleted by the physic engine // Create physics. shapeLoader is deleted by the physic engine
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
@ -473,6 +495,8 @@ namespace MWWorld
PhysicsSystem::~PhysicsSystem() PhysicsSystem::~PhysicsSystem()
{ {
if (mWaterCollisionObject.get())
mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get());
delete mEngine; delete mEngine;
} }
@ -640,9 +664,9 @@ namespace MWWorld
Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
handleToMesh[node->getName()] = mesh; handleToMesh[node->getName()] = mesh;
mEngine->createAndAdjustRigidBody( mEngine->createAndAdjustRigidBody(
mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable); mesh, node->getName(), ptr.getCellRef().getScale(), node->getPosition(), node->getOrientation(), 0, 0, false, placeable);
mEngine->createAndAdjustRigidBody( mEngine->createAndAdjustRigidBody(
mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable); mesh, node->getName(), ptr.getCellRef().getScale(), node->getPosition(), node->getOrientation(), 0, 0, true, placeable);
} }
void PhysicsSystem::addActor (const Ptr& ptr) void PhysicsSystem::addActor (const Ptr& ptr)
@ -834,14 +858,10 @@ namespace MWWorld
Ogre::Vector3(iter->first.getRefData().getPosition().pos))) Ogre::Vector3(iter->first.getRefData().getPosition().pos)))
waterCollision = true; waterCollision = true;
btStaticPlaneShape planeShape(btVector3(0,0,1), waterlevel); OEngine::Physic::PhysicActor *physicActor = mEngine->getCharacter(iter->first.getRefData().getHandle());
btCollisionObject object; if (!physicActor) // actor was already removed from the scene
object.setCollisionShape(&planeShape); continue;
physicActor->setCanWaterWalk(waterCollision);
// TODO: this seems to have a slight performance impact
if (waterCollision)
mEngine->mDynamicsWorld->addCollisionObject(&object,
0xff, OEngine::Physic::CollisionType_Actor);
// 100 points of slowfall reduce gravity by 90% (this is just a guess) // 100 points of slowfall reduce gravity by 90% (this is just a guess)
float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).getMagnitude() / 100.f) * 0.9f), 0.9f); float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).getMagnitude() / 100.f) * 0.9f), 0.9f);
@ -850,9 +870,6 @@ namespace MWWorld
world->isFlying(iter->first), world->isFlying(iter->first),
waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions); waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions);
if (waterCollision)
mEngine->mDynamicsWorld->removeCollisionObject(&object);
float heightDiff = newpos.z - oldHeight; float heightDiff = newpos.z - oldHeight;
if (heightDiff < 0) if (heightDiff < 0)
@ -928,4 +945,48 @@ namespace MWWorld
} }
} }
void PhysicsSystem::disableWater()
{
if (mWaterEnabled)
{
mWaterEnabled = false;
updateWater();
}
}
void PhysicsSystem::enableWater(float height)
{
if (!mWaterEnabled || mWaterHeight != height)
{
mWaterEnabled = true;
mWaterHeight = height;
updateWater();
}
}
void PhysicsSystem::setWaterHeight(float height)
{
if (mWaterHeight != height)
{
mWaterHeight = height;
updateWater();
}
}
void PhysicsSystem::updateWater()
{
if (mWaterCollisionObject.get())
{
mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get());
}
if (!mWaterEnabled)
return;
mWaterCollisionObject.reset(new btCollisionObject());
mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight));
mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get());
mEngine->mDynamicsWorld->addCollisionObject(mWaterCollisionObject.get(), OEngine::Physic::CollisionType_Water,
OEngine::Physic::CollisionType_Actor);
}
} }

@ -1,6 +1,8 @@
#ifndef GAME_MWWORLD_PHYSICSSYSTEM_H #ifndef GAME_MWWORLD_PHYSICSSYSTEM_H
#define GAME_MWWORLD_PHYSICSSYSTEM_H #define GAME_MWWORLD_PHYSICSSYSTEM_H
#include <memory>
#include <OgreVector3.h> #include <OgreVector3.h>
#include <btBulletCollisionCommon.h> #include <btBulletCollisionCommon.h>
@ -32,6 +34,10 @@ namespace MWWorld
PhysicsSystem (OEngine::Render::OgreRenderer &_rend); PhysicsSystem (OEngine::Render::OgreRenderer &_rend);
~PhysicsSystem (); ~PhysicsSystem ();
void enableWater(float height);
void setWaterHeight(float height);
void disableWater();
void addObject (const MWWorld::Ptr& ptr, bool placeable=false); void addObject (const MWWorld::Ptr& ptr, bool placeable=false);
void addActor (const MWWorld::Ptr& ptr); void addActor (const MWWorld::Ptr& ptr);
@ -108,6 +114,8 @@ namespace MWWorld
private: private:
void updateWater();
OEngine::Render::OgreRenderer &mRender; OEngine::Render::OgreRenderer &mRender;
OEngine::Physic::PhysicEngine* mEngine; OEngine::Physic::PhysicEngine* mEngine;
std::map<std::string, std::string> handleToMesh; std::map<std::string, std::string> handleToMesh;
@ -124,6 +132,12 @@ namespace MWWorld
float mTimeAccum; float mTimeAccum;
float mWaterHeight;
float mWaterEnabled;
std::auto_ptr<btCollisionObject> mWaterCollisionObject;
std::auto_ptr<btCollisionShape> mWaterCollisionShape;
PhysicsSystem (const PhysicsSystem&); PhysicsSystem (const PhysicsSystem&);
PhysicsSystem& operator= (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&);
}; };

@ -153,7 +153,8 @@ namespace MWWorld
Ogre::Vector3 pos(it->mNode->getPosition()); Ogre::Vector3 pos(it->mNode->getPosition());
Ogre::Vector3 newPos = pos + direction * duration * speed; Ogre::Vector3 newPos = pos + direction * duration * speed;
it->mSound->setPosition(newPos); if (it->mSound.get())
it->mSound->setPosition(newPos);
it->mNode->setPosition(newPos); it->mNode->setPosition(newPos);
@ -163,7 +164,8 @@ namespace MWWorld
// TODO: use a proper btRigidBody / btGhostObject? // TODO: use a proper btRigidBody / btGhostObject?
btVector3 from(pos.x, pos.y, pos.z); btVector3 from(pos.x, pos.y, pos.z);
btVector3 to(newPos.x, newPos.y, newPos.z); btVector3 to(newPos.x, newPos.y, newPos.z);
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to);
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to, OEngine::Physic::CollisionType_Projectile);
bool hit=false; bool hit=false;
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
@ -239,7 +241,7 @@ namespace MWWorld
// TODO: use a proper btRigidBody / btGhostObject? // TODO: use a proper btRigidBody / btGhostObject?
btVector3 from(pos.x, pos.y, pos.z); btVector3 from(pos.x, pos.y, pos.z);
btVector3 to(newPos.x, newPos.y, newPos.z); btVector3 to(newPos.x, newPos.y, newPos.z);
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to); std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to, OEngine::Physic::CollisionType_Projectile);
bool hit=false; bool hit=false;
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
@ -408,6 +410,7 @@ namespace MWWorld
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f, state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f,
MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
state.mSoundId = esm.mSound;
mMagicBolts.push_back(state); mMagicBolts.push_back(state);
return true; return true;

@ -83,7 +83,12 @@ namespace
ptr.getClass().insertObject (ptr, mPhysics); ptr.getClass().insertObject (ptr, mPhysics);
updateObjectLocalRotation(ptr, mPhysics, mRendering); updateObjectLocalRotation(ptr, mPhysics, mRendering);
MWBase::Environment::get().getWorld()->scaleObject (ptr, ptr.getCellRef().getScale()); if (ptr.getRefData().getBaseNode())
{
float scale = ptr.getCellRef().getScale();
ptr.getClass().adjustScale(ptr, scale);
mRendering.scaleObject(ptr, Ogre::Vector3(scale));
}
ptr.getClass().adjustPosition (ptr, false); ptr.getClass().adjustPosition (ptr, false);
} }
catch (const std::exception& e) catch (const std::exception& e)
@ -233,6 +238,15 @@ namespace MWWorld
insertCell (*cell, true, loadingListener); insertCell (*cell, true, loadingListener);
mRendering.cellAdded (cell); mRendering.cellAdded (cell);
bool waterEnabled = cell->getCell()->hasWater();
mRendering.setWaterEnabled(waterEnabled);
if (waterEnabled)
{
mPhysics->enableWater(cell->getWaterLevel());
mRendering.setWaterHeight(cell->getWaterLevel());
}
else
mPhysics->disableWater();
mRendering.configureAmbient(*cell); mRendering.configureAmbient(*cell);
} }

@ -1253,7 +1253,7 @@ namespace MWWorld
if (force || !isFlying(ptr)) if (force || !isFlying(ptr))
{ {
Ogre::Vector3 traced = mPhysics->traceDown(ptr, 200); Ogre::Vector3 traced = mPhysics->traceDown(ptr, 300);
if (traced.z < pos.pos[2]) if (traced.z < pos.pos[2])
pos.pos[2] = traced.z; pos.pos[2] = traced.z;
} }
@ -1660,6 +1660,7 @@ namespace MWWorld
void World::setWaterHeight(const float height) void World::setWaterHeight(const float height)
{ {
mPhysics->setWaterHeight(height);
mRendering->setWaterHeight(height); mRendering->setWaterHeight(height);
} }
@ -1994,7 +1995,7 @@ namespace MWWorld
Ogre::Vector3 playerPos(refdata.getPosition().pos); Ogre::Vector3 playerPos(refdata.getPosition().pos);
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos)) if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player))
return 2; return 2;
if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) ||
player.getClass().getNpcStats(player).isWerewolf()) player.getClass().getNpcStats(player).isWerewolf())
@ -3071,4 +3072,12 @@ namespace MWWorld
cellstore->forEach(functor); cellstore->forEach(functor);
} }
} }
bool World::isWalkingOnWater(const Ptr &actor)
{
OEngine::Physic::PhysicActor* physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle());
if (physicActor && physicActor->isWalkingOnWater())
return true;
return false;
}
} }

@ -615,6 +615,8 @@ namespace MWWorld
/// Resets all actors in the current active cells to their original location within that cell. /// Resets all actors in the current active cells to their original location within that cell.
virtual void resetActors(); virtual void resetActors();
virtual bool isWalkingOnWater (const MWWorld::Ptr& actor);
}; };
} }

@ -98,6 +98,8 @@ struct Land
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
void blank() {}
/** /**
* Actually loads data * Actually loads data
*/ */

@ -19,4 +19,10 @@ void LandTexture::save(ESMWriter &esm) const
esm.writeHNCString("DATA", mTexture); esm.writeHNCString("DATA", mTexture);
} }
void LandTexture::blank()
{
mTexture.clear();
mIndex = -1;
}
} }

@ -32,6 +32,9 @@ struct LandTexture
std::string mId, mTexture; std::string mId, mTexture;
int mIndex; int mIndex;
void blank();
///< Set record to default state (does not touch the ID).
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };

@ -53,22 +53,22 @@ AndroidPath::AndroidPath(const std::string& application_name)
boost::filesystem::path AndroidPath::getUserConfigPath() const boost::filesystem::path AndroidPath::getUserConfigPath() const
{ {
return getEnv("XDG_CONFIG_HOME", "/sdcard/morrowind/config") / mName; return getEnv("XDG_CONFIG_HOME", "/sdcard/libopenmw/config") / mName;
} }
boost::filesystem::path AndroidPath::getUserDataPath() const boost::filesystem::path AndroidPath::getUserDataPath() const
{ {
return getEnv("XDG_DATA_HOME", "/sdcard/morrowind/share") / mName; return getEnv("XDG_DATA_HOME", "/sdcard/libopenmw/share") / mName;
} }
boost::filesystem::path AndroidPath::getCachePath() const boost::filesystem::path AndroidPath::getCachePath() const
{ {
return getEnv("XDG_CACHE_HOME", "/sdcard/morrowind/cache") / mName; return getEnv("XDG_CACHE_HOME", "/sdcard/libopenmw/cache") / mName;
} }
boost::filesystem::path AndroidPath::getGlobalConfigPath() const boost::filesystem::path AndroidPath::getGlobalConfigPath() const
{ {
boost::filesystem::path globalPath("/sdcard/morrowind/"); boost::filesystem::path globalPath("/sdcard/libopenmw/");
return globalPath / mName; return globalPath / mName;
} }
@ -79,7 +79,7 @@ boost::filesystem::path AndroidPath::getLocalPath() const
boost::filesystem::path AndroidPath::getGlobalDataPath() const boost::filesystem::path AndroidPath::getGlobalDataPath() const
{ {
boost::filesystem::path globalDataPath("/sdcard/morrowind/data"); boost::filesystem::path globalDataPath("/sdcard/libopenmw/data");
return globalDataPath / mName; return globalDataPath / mName;
} }

@ -47,7 +47,7 @@
// Scroll in y direction // Scroll in y direction
float2 scrolledUV = UV + float2(0,1) * cloudAnimationTimer * 0.003; float2 scrolledUV = UV + float2(0,1) * cloudAnimationTimer * 0.003;
float4 albedo = shSample(diffuseMap1, scrolledUV) * (1-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor; float4 albedo = shSample(diffuseMap1, scrolledUV) * (1.0-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor;
shOutputColour(0) = float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity * alphaFade); shOutputColour(0) = float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity * alphaFade);
} }

@ -45,8 +45,8 @@ shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix)
shOutputColour(0).a = shSample(alphaMap, UV).a * materialDiffuse.a; shOutputColour(0).a = shSample(alphaMap, UV).a * materialDiffuse.a;
shOutputColour(0).rgb += (1-tex.a) * shOutputColour(0).a * atmosphereColour.rgb; //fill dark side of moon with atmosphereColour shOutputColour(0).rgb += (1.0-tex.a) * shOutputColour(0).a * atmosphereColour.rgb; //fill dark side of moon with atmosphereColour
shOutputColour(0).rgb += (1-materialDiffuse.a) * atmosphereColour.rgb; //fade bump shOutputColour(0).rgb += (1.0-materialDiffuse.a) * atmosphereColour.rgb; //fade bump
} }

@ -1,55 +1,55 @@
#include "core.h" #include "core.h"
#define ALPHA @shPropertyBool(shadow_transparency) #define ALPHA @shPropertyBool(shadow_transparency)
#ifdef SH_VERTEX_SHADER #ifdef SH_VERTEX_SHADER
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
#if ALPHA #if ALPHA
shVertexInput(float2, uv0) shVertexInput(float2, uv0)
shOutput(float2, UV) shOutput(float2, UV)
#endif #endif
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
shOutput(float2, depth) shOutput(float2, depth)
SH_START_PROGRAM SH_START_PROGRAM
{ {
// this is the view space position // this is the view space position
shOutputPosition = shMatrixMult(wvp, shInputPosition); shOutputPosition = shMatrixMult(wvp, shInputPosition);
// depth info for the fragment. // depth info for the fragment.
depth.x = shOutputPosition.z; depth.x = shOutputPosition.z;
depth.y = shOutputPosition.w; depth.y = shOutputPosition.w;
// clamp z to zero. seem to do the trick. :-/ // clamp z to zero. seem to do the trick. :-/
shOutputPosition.z = max(shOutputPosition.z, 0); shOutputPosition.z = max(shOutputPosition.z, 0.0);
#if ALPHA #if ALPHA
UV = uv0; UV = uv0;
#endif #endif
} }
#else #else
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
#if ALPHA #if ALPHA
shInput(float2, UV) shInput(float2, UV)
shSampler2D(texture1) shSampler2D(texture1)
#endif #endif
shInput(float2, depth) shInput(float2, depth)
SH_START_PROGRAM SH_START_PROGRAM
{ {
float finalDepth = depth.x / depth.y; float finalDepth = depth.x / depth.y;
#if ALPHA #if ALPHA
// use alpha channel of the first texture // use alpha channel of the first texture
float alpha = shSample(texture1, UV).a; float alpha = shSample(texture1, UV).a;
if (alpha < 0.5) if (alpha < 0.5)
discard; discard;
#endif #endif
shOutputColour(0) = float4(finalDepth, finalDepth, finalDepth, 1); shOutputColour(0) = float4(finalDepth, finalDepth, finalDepth, 1.0);
} }
#endif #endif

@ -1,48 +1,48 @@
#include "core.h" #include "core.h"
#ifdef SH_VERTEX_SHADER #ifdef SH_VERTEX_SHADER
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
shUniform(float4x4, worldview) @shAutoConstant(worldview, worldview_matrix) shUniform(float4x4, worldview) @shAutoConstant(worldview, worldview_matrix)
shUniform(float4x4, proj) @shAutoConstant(proj, projection_matrix) shUniform(float4x4, proj) @shAutoConstant(proj, projection_matrix)
shVertexInput(float2, uv0) shVertexInput(float2, uv0)
shOutput(float2, UV) shOutput(float2, UV)
shOutput(float, fade) shOutput(float, fade)
SH_START_PROGRAM SH_START_PROGRAM
{ {
float4x4 worldviewFixed = worldview; float4x4 worldviewFixed = worldview;
#if !SH_GLSL #if !SH_GLSL
worldviewFixed[0][3] = 0; worldviewFixed[0][3] = 0.0;
worldviewFixed[1][3] = 0; worldviewFixed[1][3] = 0.0;
worldviewFixed[2][3] = 0; worldviewFixed[2][3] = 0.0;
#else #else
worldviewFixed[3][0] = 0; worldviewFixed[3][0] = 0.0;
worldviewFixed[3][1] = 0; worldviewFixed[3][1] = 0.0;
worldviewFixed[3][2] = 0; worldviewFixed[3][2] = 0.0;
#endif #endif
shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition)); shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition));
UV = uv0; UV = uv0;
fade = (shInputPosition.z > 50) ? 1 : 0; fade = (shInputPosition.z > 50.0) ? 1.0 : 0.0;
} }
#else #else
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
shInput(float2, UV) shInput(float2, UV)
shInput(float, fade) shInput(float, fade)
shSampler2D(diffuseMap) shSampler2D(diffuseMap)
shUniform(float, nightFade) @shSharedParameter(nightFade) shUniform(float, nightFade) @shSharedParameter(nightFade)
SH_START_PROGRAM SH_START_PROGRAM
{ {
shOutputColour(0) = shSample(diffuseMap, UV) * float4(1,1,1, nightFade * fade); shOutputColour(0) = shSample(diffuseMap, UV) * float4(1,1,1, nightFade * fade);
} }
#endif #endif

@ -1,370 +1,370 @@
#include "core.h" #include "core.h"
#define SIMPLE_WATER @shGlobalSettingBool(simple_water) #define SIMPLE_WATER @shGlobalSettingBool(simple_water)
#if SIMPLE_WATER #if SIMPLE_WATER
// --------------------------------------- SIMPLE WATER --------------------------------------------------- // --------------------------------------- SIMPLE WATER ---------------------------------------------------
#define FOG @shGlobalSettingBool(fog) #define FOG @shGlobalSettingBool(fog)
#ifdef SH_VERTEX_SHADER #ifdef SH_VERTEX_SHADER
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
shVertexInput(float2, uv0) shVertexInput(float2, uv0)
shOutput(float2, UV) shOutput(float2, UV)
#if FOG #if FOG
shOutput(float, depth) shOutput(float, depth)
#endif #endif
SH_START_PROGRAM SH_START_PROGRAM
{ {
shOutputPosition = shMatrixMult(wvp, shInputPosition); shOutputPosition = shMatrixMult(wvp, shInputPosition);
UV = uv0; UV = uv0;
#if FOG #if FOG
depth = shOutputPosition.z; depth = shOutputPosition.z;
#endif #endif
} }
#else #else
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
shSampler2D(animatedTexture) shSampler2D(animatedTexture)
shInput(float2, UV) shInput(float2, UV)
shInput(float, depth) shInput(float, depth)
shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour)
shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params)
SH_START_PROGRAM SH_START_PROGRAM
{ {
shOutputColour(0).xyz = shSample(animatedTexture, UV * float2(15.0, 15.0)).xyz * float3(1.0, 1.0, 1.0); shOutputColour(0).xyz = shSample(animatedTexture, UV * float2(15.0, 15.0)).xyz * float3(1.0, 1.0, 1.0);
shOutputColour(0).w = 0.7; shOutputColour(0).w = 0.7;
#if FOG #if FOG
float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); float fogValue = shSaturate((depth - fogParams.y) * fogParams.w);
shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue);
#endif #endif
} }
#endif #endif
#else #else
// Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html ) // Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )
#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm) #define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm)
#define SHADOWS @shGlobalSettingBool(shadows) #define SHADOWS @shGlobalSettingBool(shadows)
#if SHADOWS || SHADOWS_PSSM #if SHADOWS || SHADOWS_PSSM
#include "shadows.h" #include "shadows.h"
#endif #endif
#define RIPPLES 1 #define RIPPLES 1
#define REFRACTION @shGlobalSettingBool(refraction) #define REFRACTION @shGlobalSettingBool(refraction)
#ifdef SH_VERTEX_SHADER #ifdef SH_VERTEX_SHADER
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
shVertexInput(float2, uv0) shVertexInput(float2, uv0)
shOutput(float2, UV) shOutput(float2, UV)
shOutput(float3, screenCoordsPassthrough) shOutput(float3, screenCoordsPassthrough)
shOutput(float4, position) shOutput(float4, position)
shOutput(float, depthPassthrough) shOutput(float, depthPassthrough)
#if SHADOWS #if SHADOWS
shOutput(float4, lightSpacePos0) shOutput(float4, lightSpacePos0)
shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix)
#endif #endif
#if SHADOWS_PSSM #if SHADOWS_PSSM
@shForeach(3) @shForeach(3)
shOutput(float4, lightSpacePos@shIterator) shOutput(float4, lightSpacePos@shIterator)
shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator)
@shEndForeach @shEndForeach
#endif #endif
#if SHADOWS || SHADOWS_PSSM #if SHADOWS || SHADOWS_PSSM
shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix)
#endif #endif
SH_START_PROGRAM SH_START_PROGRAM
{ {
shOutputPosition = shMatrixMult(wvp, shInputPosition); shOutputPosition = shMatrixMult(wvp, shInputPosition);
UV = uv0; UV = uv0;
#if !SH_GLSL #if !SH_GLSL
float4x4 scalemat = float4x4( 0.5, 0, 0, 0.5, float4x4 scalemat = float4x4( 0.5, 0.0, 0.0, 0.5,
0, -0.5, 0, 0.5, 0.0, -0.5, 0.0, 0.5,
0, 0, 0.5, 0.5, 0.0, 0.0, 0.5, 0.5,
0, 0, 0, 1 ); 0.0, 0.0, 0.0, 1.0 );
#else #else
mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0, mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0,
0.0, -0.5, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0); 0.5, 0.5, 0.5, 1.0);
#endif #endif
float4 texcoordProj = shMatrixMult(scalemat, shOutputPosition); float4 texcoordProj = shMatrixMult(scalemat, shOutputPosition);
screenCoordsPassthrough = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w); screenCoordsPassthrough = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w);
position = shInputPosition; position = shInputPosition;
depthPassthrough = shOutputPosition.z; depthPassthrough = shOutputPosition.z;
#if SHADOWS #if SHADOWS
lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition));
#endif #endif
#if SHADOWS_PSSM #if SHADOWS_PSSM
float4 wPos = shMatrixMult(worldMatrix, shInputPosition); float4 wPos = shMatrixMult(worldMatrix, shInputPosition);
@shForeach(3) @shForeach(3)
lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos); lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos);
@shEndForeach @shEndForeach
#endif #endif
} }
#else #else
// tweakables ---------------------------------------------------- // tweakables ----------------------------------------------------
#define VISIBILITY 1500.0 // how far you can look through water #define VISIBILITY 1500.0 // how far you can look through water
#define BIG_WAVES_X 0.3 // strength of big waves #define BIG_WAVES_X 0.3 // strength of big waves
#define BIG_WAVES_Y 0.3 #define BIG_WAVES_Y 0.3
#define MID_WAVES_X 0.3 // strength of middle sized waves #define MID_WAVES_X 0.3 // strength of middle sized waves
#define MID_WAVES_Y 0.15 #define MID_WAVES_Y 0.15
#define SMALL_WAVES_X 0.15 // strength of small waves #define SMALL_WAVES_X 0.15 // strength of small waves
#define SMALL_WAVES_Y 0.1 #define SMALL_WAVES_Y 0.1
#define WAVE_CHOPPYNESS 0.15 // wave choppyness #define WAVE_CHOPPYNESS 0.15 // wave choppyness
#define WAVE_SCALE 75 // overall wave scale #define WAVE_SCALE 75.0 // overall wave scale
#define BUMP 1.5 // overall water surface bumpiness #define BUMP 1.5 // overall water surface bumpiness
#define REFL_BUMP 0.08 // reflection distortion amount #define REFL_BUMP 0.08 // reflection distortion amount
#define REFR_BUMP 0.06 // refraction distortion amount #define REFR_BUMP 0.06 // refraction distortion amount
#define SCATTER_AMOUNT 0.3 // amount of sunlight scattering #define SCATTER_AMOUNT 0.3 // amount of sunlight scattering
#define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering #define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering
#define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction #define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction
#define SPEC_HARDNESS 256 // specular highlights hardness #define SPEC_HARDNESS 256.0 // specular highlights hardness
// --------------------------------------------------------------- // ---------------------------------------------------------------
float fresnel_dielectric(float3 Incoming, float3 Normal, float eta) float fresnel_dielectric(float3 Incoming, float3 Normal, float eta)
{ {
/* compute fresnel reflectance without explicitly computing /* compute fresnel reflectance without explicitly computing
the refracted direction */ the refracted direction */
float c = abs(dot(Incoming, Normal)); float c = abs(dot(Incoming, Normal));
float g = eta * eta - 1.0 + c * c; float g = eta * eta - 1.0 + c * c;
float result; float result;
if(g > 0.0) { if(g > 0.0) {
g = sqrt(g); g = sqrt(g);
float A =(g - c)/(g + c); float A =(g - c)/(g + c);
float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0); float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0);
result = 0.5 * A * A *(1.0 + B * B); result = 0.5 * A * A *(1.0 + B * B);
} }
else else
result = 1.0; /* TIR (no refracted component) */ result = 1.0; /* TIR (no refracted component) */
return result; return result;
} }
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
shInput(float2, UV) shInput(float2, UV)
shInput(float3, screenCoordsPassthrough) shInput(float3, screenCoordsPassthrough)
shInput(float4, position) shInput(float4, position)
shInput(float, depthPassthrough) shInput(float, depthPassthrough)
#if RIPPLES #if RIPPLES
shUniform(float3, rippleCenter) @shSharedParameter(rippleCenter, rippleCenter) shUniform(float3, rippleCenter) @shSharedParameter(rippleCenter, rippleCenter)
shUniform(float, rippleAreaLength) @shSharedParameter(rippleAreaLength, rippleAreaLength) shUniform(float, rippleAreaLength) @shSharedParameter(rippleAreaLength, rippleAreaLength)
#endif #endif
shUniform(float, far) @shAutoConstant(far, far_clip_distance) shUniform(float, far) @shAutoConstant(far, far_clip_distance)
shSampler2D(reflectionMap) shSampler2D(reflectionMap)
#if REFRACTION #if REFRACTION
shSampler2D(refractionMap) shSampler2D(refractionMap)
#endif #endif
shSampler2D(depthMap) shSampler2D(depthMap)
shSampler2D(normalMap) shSampler2D(normalMap)
#if RIPPLES #if RIPPLES
shSampler2D(rippleNormalMap) shSampler2D(rippleNormalMap)
shUniform(float4x4, wMat) @shAutoConstant(wMat, world_matrix) shUniform(float4x4, wMat) @shAutoConstant(wMat, world_matrix)
#endif #endif
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
#define WIND_SPEED windDir_windSpeed.z #define WIND_SPEED windDir_windSpeed.z
#define WIND_DIR windDir_windSpeed.xy #define WIND_DIR windDir_windSpeed.xy
shUniform(float, waterTimer) @shSharedParameter(waterTimer) shUniform(float, waterTimer) @shSharedParameter(waterTimer)
shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight) shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight)
shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0) shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0)
shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0) shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0)
shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping) shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping)
shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour)
shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params)
shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space) shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space)
#if SHADOWS #if SHADOWS
shInput(float4, lightSpacePos0) shInput(float4, lightSpacePos0)
shSampler2D(shadowMap0) shSampler2D(shadowMap0)
shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1) shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1)
#endif #endif
#if SHADOWS_PSSM #if SHADOWS_PSSM
@shForeach(3) @shForeach(3)
shInput(float4, lightSpacePos@shIterator) shInput(float4, lightSpacePos@shIterator)
shSampler2D(shadowMap@shIterator) shSampler2D(shadowMap@shIterator)
shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1)) shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1))
@shEndForeach @shEndForeach
shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints) shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints)
#endif #endif
#if SHADOWS || SHADOWS_PSSM #if SHADOWS || SHADOWS_PSSM
shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart)
#endif #endif
SH_START_PROGRAM SH_START_PROGRAM
{ {
#if SHADOWS #if SHADOWS
float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0);
#endif #endif
#if SHADOWS_PSSM #if SHADOWS_PSSM
float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints); float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints);
#endif #endif
#if SHADOWS || SHADOWS_PSSM #if SHADOWS || SHADOWS_PSSM
float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y;
float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); float fade = 1.0-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange);
shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow);
#endif #endif
#if !SHADOWS && !SHADOWS_PSSM #if !SHADOWS && !SHADOWS_PSSM
float shadow = 1.0; float shadow = 1.0;
#endif #endif
float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;
screenCoords.y = (1-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y; screenCoords.y = (1.0-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y;
float2 nCoord = float2(0,0); float2 nCoord = float2(0.0,0.0);
nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04); nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04);
float3 normal0 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0; float3 normal0 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0;
nCoord = UV * (WAVE_SCALE * 0.1) + WIND_DIR * waterTimer * (WIND_SPEED*0.08)-(normal0.xy/normal0.zz)*WAVE_CHOPPYNESS; nCoord = UV * (WAVE_SCALE * 0.1) + WIND_DIR * waterTimer * (WIND_SPEED*0.08)-(normal0.xy/normal0.zz)*WAVE_CHOPPYNESS;
float3 normal1 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.020,+waterTimer*0.015)).rgb - 1.0; float3 normal1 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.020,+waterTimer*0.015)).rgb - 1.0;
nCoord = UV * (WAVE_SCALE * 0.25) + WIND_DIR * waterTimer * (WIND_SPEED*0.07)-(normal1.xy/normal1.zz)*WAVE_CHOPPYNESS; nCoord = UV * (WAVE_SCALE * 0.25) + WIND_DIR * waterTimer * (WIND_SPEED*0.07)-(normal1.xy/normal1.zz)*WAVE_CHOPPYNESS;
float3 normal2 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.04,-waterTimer*0.03)).rgb - 1.0; float3 normal2 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.04,-waterTimer*0.03)).rgb - 1.0;
nCoord = UV * (WAVE_SCALE * 0.5) + WIND_DIR * waterTimer * (WIND_SPEED*0.09)-(normal2.xy/normal2.z)*WAVE_CHOPPYNESS; nCoord = UV * (WAVE_SCALE * 0.5) + WIND_DIR * waterTimer * (WIND_SPEED*0.09)-(normal2.xy/normal2.z)*WAVE_CHOPPYNESS;
float3 normal3 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.03,+waterTimer*0.04)).rgb - 1.0; float3 normal3 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.03,+waterTimer*0.04)).rgb - 1.0;
nCoord = UV * (WAVE_SCALE* 1.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.4)-(normal3.xy/normal3.zz)*WAVE_CHOPPYNESS; nCoord = UV * (WAVE_SCALE* 1.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.4)-(normal3.xy/normal3.zz)*WAVE_CHOPPYNESS;
float3 normal4 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.02,+waterTimer*0.1)).rgb - 1.0; float3 normal4 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.02,+waterTimer*0.1)).rgb - 1.0;
nCoord = UV * (WAVE_SCALE * 2.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.7)-(normal4.xy/normal4.zz)*WAVE_CHOPPYNESS; nCoord = UV * (WAVE_SCALE * 2.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.7)-(normal4.xy/normal4.zz)*WAVE_CHOPPYNESS;
float3 normal5 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.1,-waterTimer*0.06)).rgb - 1.0; float3 normal5 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.1,-waterTimer*0.06)).rgb - 1.0;
float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y +
normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y +
normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y); normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y);
float4 worldPosition = shMatrixMult(wMat, float4(position.xyz, 1)); float4 worldPosition = shMatrixMult(wMat, float4(position.xyz, 1));
float2 relPos = (worldPosition.xy - rippleCenter.xy) / rippleAreaLength + 0.5; float2 relPos = (worldPosition.xy - rippleCenter.xy) / rippleAreaLength + 0.5;
float3 normal_ripple = normalize(shSample(rippleNormalMap, relPos.xy).xyz * 2 - 1); float3 normal_ripple = normalize(shSample(rippleNormalMap, relPos.xy).xyz * 2.0 - 1.0);
//normal = normalize(normal + normal_ripple); //normal = normalize(normal + normal_ripple);
normal = normalize(float3(normal.x * BUMP + normal_ripple.x, normal.y * BUMP + normal_ripple.y, normal.z)); normal = normalize(float3(normal.x * BUMP + normal_ripple.x, normal.y * BUMP + normal_ripple.y, normal.z));
normal = float3(normal.x, normal.y, -normal.z); normal = float3(normal.x, normal.y, -normal.z);
// normal for sunlight scattering // normal for sunlight scattering
float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 + float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 +
normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 + normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 +
normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xyz; normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xyz;
lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z)); lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z));
lNormal = float3(lNormal.x, lNormal.y, -lNormal.z); lNormal = float3(lNormal.x, lNormal.y, -lNormal.z);
float3 lVec = normalize(sunPosition.xyz); float3 lVec = normalize(sunPosition.xyz);
float3 vVec = normalize(position.xyz - cameraPos.xyz); float3 vVec = normalize(position.xyz - cameraPos.xyz);
float isUnderwater = (cameraPos.z > 0) ? 0.0 : 1.0; float isUnderwater = (cameraPos.z > 0.0) ? 0.0 : 1.0;
// sunlight scattering // sunlight scattering
float3 pNormal = float3(0,0,1); float3 pNormal = float3(0,0,1);
float3 lR = reflect(lVec, lNormal); float3 lR = reflect(lVec, lNormal);
float3 llR = reflect(lVec, pNormal); float3 llR = reflect(lVec, pNormal);
float s = shSaturate(dot(lR, vVec)*2.0-1.2); float s = shSaturate(dot(lR, vVec)*2.0-1.2);
float lightScatter = shadow * shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); float lightScatter = shadow * shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y));
float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT)));
// fresnel // fresnel
float ior = (cameraPos.z>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); //air to water; water to air
float fresnel = fresnel_dielectric(-vVec, normal, ior); float fresnel = fresnel_dielectric(-vVec, normal, ior);
fresnel = shSaturate(fresnel); fresnel = shSaturate(fresnel);
// reflection // reflection
float3 reflection = shSample(reflectionMap, screenCoords+(normal.xy*REFL_BUMP)).rgb; float3 reflection = shSample(reflectionMap, screenCoords+(normal.xy*REFL_BUMP)).rgb;
// refraction // refraction
float3 R = reflect(vVec, normal); float3 R = reflect(vVec, normal);
#if REFRACTION #if REFRACTION
float3 refraction = shSample(refractionMap, (screenCoords-(normal.xy*REFR_BUMP))*1.0).rgb; float3 refraction = shSample(refractionMap, (screenCoords-(normal.xy*REFR_BUMP))*1.0).rgb;
// brighten up the refraction underwater // brighten up the refraction underwater
refraction = (cameraPos.z < 0) ? shSaturate(refraction * 1.5) : refraction; refraction = (cameraPos.z < 0.0) ? shSaturate(refraction * 1.5) : refraction;
#endif #endif
// specular // specular
float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow; float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow;
#if REFRACTION #if REFRACTION
shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz; shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz;
#else #else
shOutputColour(0).xyz = shLerp(reflection, float3(0.090195, 0.115685, 0.12745), (1.0-fresnel)*0.5) + specular * sunSpecular.xyz; shOutputColour(0).xyz = shLerp(reflection, float3(0.090195, 0.115685, 0.12745), (1.0-fresnel)*0.5) + specular * sunSpecular.xyz;
#endif #endif
// fog // fog
float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w);
shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue);
#if REFRACTION #if REFRACTION
shOutputColour(0).w = 1; shOutputColour(0).w = 1.0;
#else #else
shOutputColour(0).w = shSaturate(fresnel*2 + specular); shOutputColour(0).w = shSaturate(fresnel*2.0 + specular);
#endif #endif
} }
#endif #endif
#endif #endif

@ -74,6 +74,8 @@ namespace Physic
, mExternalCollisionMode(true) , mExternalCollisionMode(true)
, mForce(0.0f) , mForce(0.0f)
, mScale(scale) , mScale(scale)
, mWalkingOnWater(false)
, mCanWaterWalk(false)
{ {
if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation)) if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation))
{ {
@ -103,8 +105,7 @@ namespace Physic
setPosition(position); setPosition(position);
setRotation(rotation); setRotation(rotation);
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, updateCollisionMask();
CollisionType_Actor|CollisionType_World|CollisionType_HeightMap);
} }
PhysicActor::~PhysicActor() PhysicActor::~PhysicActor()
@ -123,10 +124,22 @@ namespace Physic
void PhysicActor::enableCollisionBody(bool collision) void PhysicActor::enableCollisionBody(bool collision)
{ {
assert(mBody); if (mExternalCollisionMode != collision)
if(collision && !mExternalCollisionMode) enableCollisionBody(); {
if(!collision && mExternalCollisionMode) disableCollisionBody(); mExternalCollisionMode = collision;
mExternalCollisionMode = collision; updateCollisionMask();
}
}
void PhysicActor::updateCollisionMask()
{
mEngine->mDynamicsWorld->removeRigidBody(mBody);
int collisionMask = CollisionType_World | CollisionType_HeightMap;
if (mExternalCollisionMode)
collisionMask |= CollisionType_Actor | CollisionType_Projectile;
if (mCanWaterWalk)
collisionMask |= CollisionType_Water;
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, collisionMask);
} }
const Ogre::Vector3& PhysicActor::getPosition() const const Ogre::Vector3& PhysicActor::getPosition() const
@ -178,18 +191,23 @@ namespace Physic
mOnGround = grounded; mOnGround = grounded;
} }
void PhysicActor::disableCollisionBody() bool PhysicActor::isWalkingOnWater() const
{ {
mEngine->mDynamicsWorld->removeRigidBody(mBody); return mWalkingOnWater;
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor,
CollisionType_World|CollisionType_HeightMap);
} }
void PhysicActor::enableCollisionBody() void PhysicActor::setWalkingOnWater(bool walkingOnWater)
{ {
mEngine->mDynamicsWorld->removeRigidBody(mBody); mWalkingOnWater = walkingOnWater;
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, }
CollisionType_Actor|CollisionType_World|CollisionType_HeightMap);
void PhysicActor::setCanWaterWalk(bool waterWalk)
{
if (waterWalk != mCanWaterWalk)
{
mCanWaterWalk = waterWalk;
updateCollisionMask();
}
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -381,7 +399,7 @@ namespace Physic
mHeightFieldMap [name] = hf; mHeightFieldMap [name] = hf;
mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap, mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap,
CollisionType_Actor|CollisionType_Raycasting); CollisionType_Actor|CollisionType_Raycasting|CollisionType_Projectile);
} }
void PhysicEngine::removeHeightField(int x, int y) void PhysicEngine::removeHeightField(int x, int y)
@ -494,7 +512,7 @@ namespace Physic
{ {
assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end()); assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end());
mRaycastingObjectMap[name] = body; mRaycastingObjectMap[name] = body;
mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting); mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_Projectile);
} }
return body; return body;
@ -800,10 +818,10 @@ namespace Physic
return std::make_pair(false, 1); return std::make_pair(false, 1);
} }
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to) std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to, int filterGroup)
{ {
MyRayResultCallback resultCallback1; MyRayResultCallback resultCallback1;
resultCallback1.m_collisionFilterGroup = 0xff; resultCallback1.m_collisionFilterGroup = filterGroup;
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor|CollisionType_HeightMap; resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor|CollisionType_HeightMap;
mDynamicsWorld->rayTest(from, to, resultCallback1); mDynamicsWorld->rayTest(from, to, resultCallback1);
std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results; std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results;

@ -46,7 +46,9 @@ namespace Physic
CollisionType_World = 1<<0, //<Collide with world objects CollisionType_World = 1<<0, //<Collide with world objects
CollisionType_Actor = 1<<1, //<Collide sith actors CollisionType_Actor = 1<<1, //<Collide sith actors
CollisionType_HeightMap = 1<<2, //<collide with heightmap CollisionType_HeightMap = 1<<2, //<collide with heightmap
CollisionType_Raycasting = 1<<3 //Still used? CollisionType_Raycasting = 1<<3,
CollisionType_Projectile = 1<<4,
CollisionType_Water = 1<<5
}; };
/** /**
@ -130,9 +132,20 @@ namespace Physic
return mBody; return mBody;
} }
/// Sets whether this actor should be able to collide with the water surface
void setCanWaterWalk(bool waterWalk);
/// Sets whether this actor has been walking on the water surface in the last frame
void setWalkingOnWater(bool walkingOnWater);
bool isWalkingOnWater() const;
private: private:
void disableCollisionBody(); /// Removes then re-adds the collision body to the dynamics world
void enableCollisionBody(); void updateCollisionMask();
bool mCanWaterWalk;
bool mWalkingOnWater;
boost::shared_ptr<btCollisionShape> mShape; boost::shared_ptr<btCollisionShape> mShape;
@ -288,7 +301,7 @@ namespace Physic
/** /**
* Return all objects hit by a ray. * Return all objects hit by a ray.
*/ */
std::vector< std::pair<float, std::string> > rayTest2(const btVector3 &from, const btVector3 &to); std::vector< std::pair<float, std::string> > rayTest2(const btVector3 &from, const btVector3 &to, int filterGroup=0xff);
std::pair<bool, float> sphereCast (float radius, btVector3& from, btVector3& to); std::pair<bool, float> sphereCast (float radius, btVector3& from, btVector3& to);
///< @return (hit, relative distance) ///< @return (hit, relative distance)

@ -105,6 +105,7 @@ void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Og
// Inherit the actor's collision group and mask // Inherit the actor's collision group and mask
newTraceCallback.m_collisionFilterGroup = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterGroup; newTraceCallback.m_collisionFilterGroup = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterGroup;
newTraceCallback.m_collisionFilterMask = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterMask; newTraceCallback.m_collisionFilterMask = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterMask;
newTraceCallback.m_collisionFilterMask &= ~CollisionType_Actor;
btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z); btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z);

Loading…
Cancel
Save