# Conflicts:
#	docs/source/openmw-mods/index.rst
pull/124/head
Ryan Tucker 8 years ago
commit bf805813e5

@ -1,6 +1,6 @@
os: os:
- linux - linux
- osx # - osx
osx_image: xcode7.2 osx_image: xcode7.2
language: cpp language: cpp
sudo: required sudo: required

@ -23,10 +23,12 @@ Programmers
Alexander Olofsson (Ace) Alexander Olofsson (Ace)
Allofich Allofich
AnyOldName3 AnyOldName3
Aussiemon
Austin Salgat (Salgat) Austin Salgat (Salgat)
Artem Kotsynyak (greye) Artem Kotsynyak (greye)
artemutin artemutin
Arthur Moore (EmperorArthur) Arthur Moore (EmperorArthur)
Assumeru
athile athile
Ben Shealy (bentsherman) Ben Shealy (bentsherman)
Bret Curtis (psi29a) Bret Curtis (psi29a)
@ -73,6 +75,7 @@ Programmers
Karl-Felix Glatzer (k1ll) Karl-Felix Glatzer (k1ll)
Kevin Poitra (PuppyKevin) Kevin Poitra (PuppyKevin)
Koncord Koncord
Kurnevsky Evgeny (kurnevsky)
Lars Söderberg (Lazaroth) Lars Söderberg (Lazaroth)
lazydev lazydev
Leon Saunders (emoose) Leon Saunders (emoose)
@ -119,6 +122,7 @@ Programmers
Scott Howard Scott Howard
Sebastian Wick (swick) Sebastian Wick (swick)
Sergey Shambir Sergey Shambir
ShadowRadiance
sir_herrbatka sir_herrbatka
smbas smbas
Stefan Galowicz (bogglez) Stefan Galowicz (bogglez)

@ -1,10 +1,11 @@
#!/bin/sh #!/bin/sh
brew update brew update
brew rm cmake || true brew rm cmake || true
brew rm pkgconfig || true brew rm pkgconfig || true
brew rm qt5 || true brew rm qt5 || true
brew install cmake pkgconfig qt5 brew install cmake pkgconfig qt55
curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

@ -4,13 +4,12 @@ export CXX=clang++
export CC=clang export CC=clang
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
QT_PATH="/usr/local/opt/qt5" QT_PATH="/usr/local/opt/qt55"
mkdir build mkdir build
cd build cd build
cmake \ cmake \
-D CMAKE_EXE_LINKER_FLAGS="-lz" \
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \ -D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
-D CMAKE_OSX_SYSROOT="macosx10.11" \ -D CMAKE_OSX_SYSROOT="macosx10.11" \

@ -96,7 +96,7 @@ endif()
# Set up common paths # Set up common paths
if (APPLE) if (APPLE)
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") set(OPENMW_RESOURCE_FILES "../Resources/resources" CACHE PATH "location of OpenMW resources files")
elseif(UNIX) elseif(UNIX)
# Paths # Paths
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
@ -286,6 +286,11 @@ endif (APPLE)
# Set up DEBUG define # Set up DEBUG define
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1)
if (NOT APPLE)
set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR})
set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})
endif ()
add_subdirectory(files/) add_subdirectory(files/)
# Specify build paths # Specify build paths
@ -307,11 +312,15 @@ endif (APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
"${OpenMW_BINARY_DIR}/settings-default.cfg") "${OpenMW_BINARY_DIR}/settings-default.cfg")
if (NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg") "${OpenMW_BINARY_DIR}/openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install") "${OpenMW_BINARY_DIR}/openmw.cfg.install")
else ()
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg")
endif ()
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
"${OpenMW_BINARY_DIR}/openmw-cs.cfg") "${OpenMW_BINARY_DIR}/openmw-cs.cfg")
@ -419,8 +428,10 @@ IF(NOT WIN32 AND NOT APPLE)
ENDIF(NOT WIN32 AND NOT APPLE) ENDIF(NOT WIN32 AND NOT APPLE)
if(WIN32) if(WIN32)
FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll") FILE(GLOB dll_files_debug "${OpenMW_BINARY_DIR}/Debug/*.dll")
INSTALL(FILES ${dll_files} DESTINATION ".") FILE(GLOB dll_files_release "${OpenMW_BINARY_DIR}/Release/*.dll")
INSTALL(FILES ${dll_files_debug} DESTINATION "." CONFIGURATIONS Debug)
INSTALL(FILES ${dll_files_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
@ -429,36 +440,23 @@ if(WIN32)
"${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt" "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt"
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
DESTINATION ".") DESTINATION ".")
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-launcher.exe" DESTINATION ".")
ENDIF(BUILD_LAUNCHER)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-iniimporter.exe" DESTINATION ".")
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".")
ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".")
ENDIF(BUILD_WIZARD)
if(BUILD_MYGUI_PLUGIN) if(BUILD_MYGUI_PLUGIN)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION ".") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Debug/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
ENDIF(BUILD_MYGUI_PLUGIN) ENDIF(BUILD_MYGUI_PLUGIN)
IF(DESIRED_QT_VERSION MATCHES 5) IF(DESIRED_QT_VERSION MATCHES 5)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION ".") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
ENDIF() ENDIF()
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
FILE(GLOB plugin_dir "${OpenMW_BINARY_DIR}/Release/osgPlugins-*") FILE(GLOB plugin_dir_debug "${OpenMW_BINARY_DIR}/Debug/osgPlugins-*")
INSTALL(DIRECTORY ${plugin_dir} DESTINATION ".") FILE(GLOB plugin_dir_release "${OpenMW_BINARY_DIR}/Release/osgPlugins-*")
INSTALL(DIRECTORY ${plugin_dir_debug} DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY ${plugin_dir_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
SET(CPACK_GENERATOR "NSIS") SET(CPACK_GENERATOR "NSIS")
SET(CPACK_PACKAGE_NAME "OpenMW") SET(CPACK_PACKAGE_NAME "OpenMW")
@ -725,14 +723,7 @@ if (APPLE)
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
endif () endif ()
set(INSTALL_SUBDIR OpenMW) install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime)
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
@ -740,8 +731,8 @@ if (APPLE)
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_NAME}")
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${OPENCS_BUNDLE_NAME}")
install(CODE " install(CODE "
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
@ -785,8 +776,8 @@ if (APPLE)
set(${plugins_var} ${PLUGINS} PARENT_SCOPE) set(${plugins_var} ${PLUGINS} PARENT_SCOPE)
endfunction (install_plugins_for_bundle) endfunction (install_plugins_for_bundle)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS) install_plugins_for_bundle("${APP_BUNDLE_NAME}" PLUGINS)
install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) install_plugins_for_bundle("${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
@ -828,3 +819,4 @@ if (DOXYGEN_FOUND)
WORKING_DIRECTORY ${OpenMW_BINARY_DIR} WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
endif () endif ()

@ -42,3 +42,7 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(openmw-essimporter gcov) target_link_libraries(openmw-essimporter gcov)
endif() endif()
if (WIN32)
INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".")
endif(WIN32)

@ -271,23 +271,34 @@ private:
class ConvertPCDT : public Converter class ConvertPCDT : public Converter
{ {
public: public:
ConvertPCDT() : mFirstPersonCam(true) {} ConvertPCDT()
: mFirstPersonCam(true),
mTeleportingEnabled(true),
mLevitationEnabled(true)
{}
virtual void read(ESM::ESMReader &esm) virtual void read(ESM::ESMReader &esm)
{ {
PCDT pcdt; PCDT pcdt;
pcdt.load(esm); pcdt.load(esm);
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam); convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);
} }
virtual void write(ESM::ESMWriter &esm) virtual void write(ESM::ESMWriter &esm)
{ {
esm.startRecord(ESM::REC_ENAB);
esm.writeHNT("TELE", mTeleportingEnabled);
esm.writeHNT("LEVT", mLevitationEnabled);
esm.endRecord(ESM::REC_ENAB);
esm.startRecord(ESM::REC_CAM_); esm.startRecord(ESM::REC_CAM_);
esm.writeHNT("FIRS", mFirstPersonCam); esm.writeHNT("FIRS", mFirstPersonCam);
esm.endRecord(ESM::REC_CAM_); esm.endRecord(ESM::REC_CAM_);
} }
private: private:
bool mFirstPersonCam; bool mFirstPersonCam;
bool mTeleportingEnabled;
bool mLevitationEnabled;
}; };
class ConvertCNTC : public Converter class ConvertCNTC : public Converter

@ -5,7 +5,7 @@
namespace ESSImport namespace ESSImport
{ {
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam) void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls)
{ {
out.mBirthsign = pcdt.mBirthsign; out.mBirthsign = pcdt.mBirthsign;
out.mObject.mNpcStats.mBounty = pcdt.mBounty; out.mObject.mNpcStats.mBounty = pcdt.mBounty;
@ -25,18 +25,28 @@ namespace ESSImport
out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i]; out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress; out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon) if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawn)
out.mObject.mCreatureStats.mDrawState = 1; out.mObject.mCreatureStats.mDrawState = 1;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell) if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawn)
out.mObject.mCreatureStats.mDrawState = 2; out.mObject.mCreatureStats.mDrawState = 2;
firstPersonCam = !(pcdt.mPNAM.mCameraFlags & PCDT::CameraFlag_ThirdPerson); firstPersonCam = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ThirdPerson);
teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);
levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin(); for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it) it != pcdt.mKnownDialogueTopics.end(); ++it)
{ {
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
} }
controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;
controls.mControlsDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ControlsDisabled;
controls.mJumpingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_JumpingDisabled;
controls.mLookingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LookingDisabled;
controls.mVanityModeDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_VanityModeDisabled;
controls.mWeaponDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawingDisabled;
controls.mSpellDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawingDisabled;
} }
} }

@ -4,11 +4,12 @@
#include "importplayer.hpp" #include "importplayer.hpp"
#include <components/esm/player.hpp> #include <components/esm/player.hpp>
#include <components/esm/controlsstate.hpp>
namespace ESSImport namespace ESSImport
{ {
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam); void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls);
} }

@ -51,6 +51,7 @@ namespace
{ {
for (int x=0; x<128; ++x) for (int x=0; x<128; ++x)
{ {
assert(image->data(x,y));
*(image->data(x,y)+2) = *it++; *(image->data(x,y)+2) = *it++;
*(image->data(x,y)+1) = *it++; *(image->data(x,y)+1) = *it++;
*image->data(x,y) = *it++; *image->data(x,y) = *it++;
@ -422,6 +423,10 @@ namespace ESSImport
writer.startRecord (ESM::REC_DIAS); writer.startRecord (ESM::REC_DIAS);
context.mDialogueState.save(writer); context.mDialogueState.save(writer);
writer.endRecord(ESM::REC_DIAS); writer.endRecord(ESM::REC_DIAS);
writer.startRecord(ESM::REC_INPU);
context.mControlsState.save(writer);
writer.endRecord(ESM::REC_INPU);
} }

@ -9,6 +9,7 @@
#include <components/esm/globalmap.hpp> #include <components/esm/globalmap.hpp>
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/controlsstate.hpp>
#include "importnpcc.hpp" #include "importnpcc.hpp"
#include "importcrec.hpp" #include "importcrec.hpp"
@ -32,6 +33,8 @@ namespace ESSImport
ESM::DialogueState mDialogueState; ESM::DialogueState mDialogueState;
ESM::ControlsState mControlsState;
// cells which should show an explored overlay on the global map // cells which should show an explored overlay on the global map
std::set<std::pair<int, int> > mExploredCells; std::set<std::pair<int, int> > mExploredCells;

@ -38,14 +38,20 @@ struct PCDT
std::vector<std::string> mKnownDialogueTopics; std::vector<std::string> mKnownDialogueTopics;
enum DrawState_ enum PlayerFlags
{ {
DrawState_Weapon = 0x80, PlayerFlags_ViewSwitchDisabled = 0x1,
DrawState_Spell = 0x100 PlayerFlags_ControlsDisabled = 0x4,
}; PlayerFlags_WeaponDrawn = 0x80,
enum CameraFlags PlayerFlags_SpellDrawn = 0x100,
{ PlayerFlags_JumpingDisabled = 0x1000,
CameraFlag_ThirdPerson = 0x2 PlayerFlags_LookingDisabled = 0x2000,
PlayerFlags_VanityModeDisabled = 0x4000,
PlayerFlags_WeaponDrawingDisabled = 0x8000,
PlayerFlags_SpellDrawingDisabled = 0x10000,
PlayerFlags_ThirdPerson = 0x20000,
PlayerFlags_TeleportingDisabled = 0x40000,
PlayerFlags_LevitationDisabled = 0x80000
}; };
#pragma pack(push) #pragma pack(push)
@ -62,8 +68,7 @@ struct PCDT
struct PNAM struct PNAM
{ {
short mDrawState; // DrawState int mPlayerFlags; // controls, camera and draw state
short mCameraFlags; // CameraFlags
unsigned int mLevelProgress; unsigned int mLevelProgress;
float mSkillProgress[27]; // skill progress, non-uniform scaled float mSkillProgress[27]; // skill progress, non-uniform scaled
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute unsigned char mSkillIncreases[8]; // number of skill increases for each attribute

@ -13,7 +13,7 @@ namespace ESM
namespace ESSImport namespace ESSImport
{ {
/// Local variable assigments for a running script /// Local variable assignments for a running script
struct SCRI struct SCRI
{ {
std::string mScript; std::string mScript;

@ -87,6 +87,10 @@ add_executable(openmw-launcher
${UI_HDRS} ${UI_HDRS}
) )
if (WIN32)
INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".")
endif (WIN32)
target_link_libraries(openmw-launcher target_link_libraries(openmw-launcher
${SDL2_LIBRARY_ONLY} ${SDL2_LIBRARY_ONLY}
components components

@ -35,14 +35,6 @@ int main(int argc, char *argv[])
// Now we make sure the current dir is set to application path // Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
#endif
QDir::setCurrent(dir.absolutePath()); QDir::setCurrent(dir.absolutePath());
Launcher::MainDialog mainWin; Launcher::MainDialog mainWin;

@ -293,6 +293,7 @@ bool Launcher::MainDialog::setupGameSettings()
{ {
mGameSettings.clear(); mGameSettings.clear();
QString localPath = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str());
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
@ -320,13 +321,13 @@ bool Launcher::MainDialog::setupGameSettings()
// Now the rest - priority: user > local > global // Now the rest - priority: user > local > global
QStringList paths; QStringList paths;
paths.append(globalPath + QString("openmw.cfg")); paths.append(globalPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg")); paths.append(localPath + QString("openmw.cfg"));
paths.append(userPath + QString("openmw.cfg")); paths.append(userPath + QString("openmw.cfg"));
foreach (const QString &path, paths) { foreach (const QString &path2, paths) {
qDebug() << "Loading config file:" << path.toUtf8().constData(); qDebug() << "Loading config file:" << path2.toUtf8().constData();
QFile file(path); file.setFileName(path2);
if (file.exists()) { if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
cfgError(tr("Error opening OpenMW configuration file"), cfgError(tr("Error opening OpenMW configuration file"),
@ -346,13 +347,13 @@ bool Launcher::MainDialog::setupGameSettings()
QStringList dataDirs; QStringList dataDirs;
// Check if the paths actually contain data files // Check if the paths actually contain data files
foreach (const QString path, mGameSettings.getDataDirs()) { foreach (const QString path3, mGameSettings.getDataDirs()) {
QDir dir(path); QDir dir(path3);
QStringList filters; QStringList filters;
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
if (!dir.entryList(filters).isEmpty()) if (!dir.entryList(filters).isEmpty())
dataDirs.append(path); dataDirs.append(path3);
} }
if (dataDirs.isEmpty()) if (dataDirs.isEmpty())

@ -22,7 +22,8 @@ target_link_libraries(openmw-iniimporter
if (WIN32) if (WIN32)
target_link_libraries(openmw-iniimporter target_link_libraries(openmw-iniimporter
${Boost_LOCALE_LIBRARY}) ${Boost_LOCALE_LIBRARY})
endif() INSTALL(TARGETS openmw-iniimporter RUNTIME DESTINATION ".")
endif(WIN32)
if (MINGW) if (MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")

@ -162,9 +162,15 @@ endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(APPLE) if(APPLE)
set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns) set (OPENCS_MAC_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns")
set (OPENCS_CFG "${OpenMW_BINARY_DIR}/openmw-cs.cfg")
set (OPENCS_DEFAULT_FILTERS_FILE "${OpenMW_BINARY_DIR}/resources/defaultfilters")
set (OPENCS_OPENMW_CFG "${OpenMW_BINARY_DIR}/openmw.cfg")
else() else()
set (OPENCS_MAC_ICON "") set (OPENCS_MAC_ICON "")
set (OPENCS_CFG "")
set (OPENCS_DEFAULT_FILTERS_FILE "")
set (OPENCS_OPENMW_CFG "")
endif(APPLE) endif(APPLE)
add_executable(openmw-cs add_executable(openmw-cs
@ -174,12 +180,23 @@ add_executable(openmw-cs
${OPENCS_MOC_SRC} ${OPENCS_MOC_SRC}
${OPENCS_RES_SRC} ${OPENCS_RES_SRC}
${OPENCS_MAC_ICON} ${OPENCS_MAC_ICON}
${OPENCS_CFG}
${OPENCS_DEFAULT_FILTERS_FILE}
${OPENCS_OPENMW_CFG}
) )
if(APPLE) if(APPLE)
set(OPENCS_BUNDLE_NAME "OpenMW-CS")
set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources")
set(OPENMW_MYGUI_FILES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)
set_target_properties(openmw-cs PROPERTIES set_target_properties(openmw-cs PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
OUTPUT_NAME "OpenMW-CS" OUTPUT_NAME ${OPENCS_BUNDLE_NAME}
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns" MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
@ -190,6 +207,16 @@ if(APPLE)
set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources) MACOSX_PACKAGE_LOCATION Resources)
set_source_files_properties(${OPENCS_CFG} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
set_source_files_properties(${OPENCS_DEFAULT_FILTERS_FILE} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources/resources)
set_source_files_properties(${OPENCS_OPENMW_CFG} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
add_custom_command(TARGET openmw-cs
POST_BUILD
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${OPENCS_BUNDLE_RESOURCES_DIR}/resources")
endif(APPLE) endif(APPLE)
target_link_libraries(openmw-cs target_link_libraries(openmw-cs
@ -223,9 +250,18 @@ endif()
if (WIN32) if (WIN32)
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
endif() endif()
if (MSVC)
# Debug version needs increased number of sections beyond 2^16
if (CMAKE_CL_64)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
endif (CMAKE_CL_64)
endif (MSVC)
if(APPLE) if(APPLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE)
endif() endif()

@ -62,11 +62,6 @@ int main(int argc, char *argv[])
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath()); QDir::setCurrent(dir.absolutePath());
#endif #endif

@ -29,7 +29,7 @@ namespace CSMPrefs
enum SecondaryMode enum SecondaryMode
{ {
SM_Replace, ///< The secondary signal replaces the regular signal when the modifier is active SM_Replace, ///< The secondary signal replaces the regular signal when the modifier is active
SM_Detach, ///< The secondary signal is emitted independant of the regular signal, even if not active SM_Detach, ///< The secondary signal is emitted independent of the regular signal, even if not active
SM_Ignore ///< The secondary signal will not ever be emitted SM_Ignore ///< The secondary signal will not ever be emitted
}; };

@ -184,7 +184,7 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes
CSMWorld::LandTexture texture = CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get(); mState.mSource.getData().getLandTextures().getRecord (index).get();
std::ostringstream stream; stream.clear();
stream << mNext->second-1 << "_0"; stream << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1; texture.mIndex = mNext->second-1;

@ -659,7 +659,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
{ {
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
{ {
messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happend? messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen?
return; return;
} }
@ -915,7 +915,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck(
id + " contains non-existing item (" + itemName + ")")); id + " contains non-existing item (" + itemName + ")"));
else else
{ {
// Needs to accomodate Containers, Creatures, and NPCs // Needs to accommodate containers, creatures, and NPCs
switch (localIndex.second) switch (localIndex.second)
{ {
case CSMWorld::UniversalId::Type_Potion: case CSMWorld::UniversalId::Type_Potion:

@ -1627,264 +1627,264 @@ const char * CSMWorld::DefaultGmsts::OptionalStrings[CSMWorld::DefaultGmsts::Opt
const float CSMWorld::DefaultGmsts::FloatsDefaultValues[CSMWorld::DefaultGmsts::FloatCount] = const float CSMWorld::DefaultGmsts::FloatsDefaultValues[CSMWorld::DefaultGmsts::FloatCount] =
{ {
0.3, // fAIFleeFleeMult 0.3f, // fAIFleeFleeMult
7.0, // fAIFleeHealthMult 7.0f, // fAIFleeHealthMult
3.0, // fAIMagicSpellMult 3.0f, // fAIMagicSpellMult
1.0, // fAIMeleeArmorMult 1.0f, // fAIMeleeArmorMult
1.0, // fAIMeleeSummWeaponMult 1.0f, // fAIMeleeSummWeaponMult
2.0, // fAIMeleeWeaponMult 2.0f, // fAIMeleeWeaponMult
5.0, // fAIRangeMagicSpellMult 5.0f, // fAIRangeMagicSpellMult
5.0, // fAIRangeMeleeWeaponMult 5.0f, // fAIRangeMeleeWeaponMult
2000.0, // fAlarmRadius 2000.0f, // fAlarmRadius
1.0, // fAthleticsRunBonus 1.0f, // fAthleticsRunBonus
40.0, // fAudioDefaultMaxDistance 40.0f, // fAudioDefaultMaxDistance
5.0, // fAudioDefaultMinDistance 5.0f, // fAudioDefaultMinDistance
50.0, // fAudioMaxDistanceMult 50.0f, // fAudioMaxDistanceMult
20.0, // fAudioMinDistanceMult 20.0f, // fAudioMinDistanceMult
60.0, // fAudioVoiceDefaultMaxDistance 60.0f, // fAudioVoiceDefaultMaxDistance
10.0, // fAudioVoiceDefaultMinDistance 10.0f, // fAudioVoiceDefaultMinDistance
50.0, // fAutoPCSpellChance 50.0f, // fAutoPCSpellChance
80.0, // fAutoSpellChance 80.0f, // fAutoSpellChance
50.0, // fBargainOfferBase 50.0f, // fBargainOfferBase
-4.0, // fBargainOfferMulti -4.0f, // fBargainOfferMulti
24.0, // fBarterGoldResetDelay 24.0f, // fBarterGoldResetDelay
1.75, // fBaseRunMultiplier 1.75f, // fBaseRunMultiplier
1.25, // fBlockStillBonus 1.25f, // fBlockStillBonus
150.0, // fBribe1000Mod 150.0f, // fBribe1000Mod
75.0, // fBribe100Mod 75.0f, // fBribe100Mod
35.0, // fBribe10Mod 35.0f, // fBribe10Mod
60.0, // fCombatAngleXY 60.0f, // fCombatAngleXY
60.0, // fCombatAngleZ 60.0f, // fCombatAngleZ
0.25, // fCombatArmorMinMult 0.25f, // fCombatArmorMinMult
-90.0, // fCombatBlockLeftAngle -90.0f, // fCombatBlockLeftAngle
30.0, // fCombatBlockRightAngle 30.0f, // fCombatBlockRightAngle
4.0, // fCombatCriticalStrikeMult 4.0f, // fCombatCriticalStrikeMult
0.1, // fCombatDelayCreature 0.1f, // fCombatDelayCreature
0.1, // fCombatDelayNPC 0.1f, // fCombatDelayNPC
128.0, // fCombatDistance 128.0f, // fCombatDistance
0.3, // fCombatDistanceWerewolfMod 0.3f, // fCombatDistanceWerewolfMod
30.0, // fCombatForceSideAngle 30.0f, // fCombatForceSideAngle
0.2, // fCombatInvisoMult 0.2f, // fCombatInvisoMult
1.5, // fCombatKODamageMult 1.5f, // fCombatKODamageMult
45.0, // fCombatTorsoSideAngle 45.0f, // fCombatTorsoSideAngle
0.3, // fCombatTorsoStartPercent 0.3f, // fCombatTorsoStartPercent
0.8, // fCombatTorsoStopPercent 0.8f, // fCombatTorsoStopPercent
15.0, // fConstantEffectMult 15.0f, // fConstantEffectMult
72.0, // fCorpseClearDelay 72.0f, // fCorpseClearDelay
72.0, // fCorpseRespawnDelay 72.0f, // fCorpseRespawnDelay
0.5, // fCrimeGoldDiscountMult 0.5f, // fCrimeGoldDiscountMult
0.9, // fCrimeGoldTurnInMult 0.9f, // fCrimeGoldTurnInMult
1.0, // fCrimeStealing 1.0f, // fCrimeStealing
0.5, // fDamageStrengthBase 0.5f, // fDamageStrengthBase
0.1, // fDamageStrengthMult 0.1f, // fDamageStrengthMult
5.0, // fDifficultyMult 5.0f, // fDifficultyMult
2.5, // fDiseaseXferChance 2.5f, // fDiseaseXferChance
-10.0, // fDispAttacking -10.0f, // fDispAttacking
-1.0, // fDispBargainFailMod -1.0f, // fDispBargainFailMod
1.0, // fDispBargainSuccessMod 1.0f, // fDispBargainSuccessMod
0.0, // fDispCrimeMod 0.0f, // fDispCrimeMod
-10.0, // fDispDiseaseMod -10.0f, // fDispDiseaseMod
3.0, // fDispFactionMod 3.0f, // fDispFactionMod
1.0, // fDispFactionRankBase 1.0f, // fDispFactionRankBase
0.5, // fDispFactionRankMult 0.5f, // fDispFactionRankMult
1.0, // fDispositionMod 1.0f, // fDispositionMod
50.0, // fDispPersonalityBase 50.0f, // fDispPersonalityBase
0.5, // fDispPersonalityMult 0.5f, // fDispPersonalityMult
-25.0, // fDispPickPocketMod -25.0f, // fDispPickPocketMod
5.0, // fDispRaceMod 5.0f, // fDispRaceMod
-0.5, // fDispStealing -0.5f, // fDispStealing
-5.0, // fDispWeaponDrawn -5.0f, // fDispWeaponDrawn
0.5, // fEffectCostMult 0.5f, // fEffectCostMult
0.1, // fElementalShieldMult 0.1f, // fElementalShieldMult
3.0, // fEnchantmentChanceMult 3.0f, // fEnchantmentChanceMult
0.5, // fEnchantmentConstantChanceMult 0.5f, // fEnchantmentConstantChanceMult
100.0, // fEnchantmentConstantDurationMult 100.0f, // fEnchantmentConstantDurationMult
0.1, // fEnchantmentMult 0.1f, // fEnchantmentMult
1000.0, // fEnchantmentValueMult 1000.0f, // fEnchantmentValueMult
0.3, // fEncumberedMoveEffect 0.3f, // fEncumberedMoveEffect
5.0, // fEncumbranceStrMult 5.0f, // fEncumbranceStrMult
0.04, // fEndFatigueMult 0.04f, // fEndFatigueMult
0.25, // fFallAcroBase 0.25f, // fFallAcroBase
0.01, // fFallAcroMult 0.01f, // fFallAcroMult
400.0, // fFallDamageDistanceMin 400.0f, // fFallDamageDistanceMin
0.0, // fFallDistanceBase 0.0f, // fFallDistanceBase
0.07, // fFallDistanceMult 0.07f, // fFallDistanceMult
2.0, // fFatigueAttackBase 2.0f, // fFatigueAttackBase
0.0, // fFatigueAttackMult 0.0f, // fFatigueAttackMult
1.25, // fFatigueBase 1.25f, // fFatigueBase
4.0, // fFatigueBlockBase 4.0f, // fFatigueBlockBase
0.0, // fFatigueBlockMult 0.0f, // fFatigueBlockMult
5.0, // fFatigueJumpBase 5.0f, // fFatigueJumpBase
0.0, // fFatigueJumpMult 0.0f, // fFatigueJumpMult
0.5, // fFatigueMult 0.5f, // fFatigueMult
2.5, // fFatigueReturnBase 2.5f, // fFatigueReturnBase
0.02, // fFatigueReturnMult 0.02f, // fFatigueReturnMult
5.0, // fFatigueRunBase 5.0f, // fFatigueRunBase
2.0, // fFatigueRunMult 2.0f, // fFatigueRunMult
1.5, // fFatigueSneakBase 1.5f, // fFatigueSneakBase
1.5, // fFatigueSneakMult 1.5f, // fFatigueSneakMult
0.0, // fFatigueSpellBase 0.0f, // fFatigueSpellBase
0.0, // fFatigueSpellCostMult 0.0f, // fFatigueSpellCostMult
0.0, // fFatigueSpellMult 0.0f, // fFatigueSpellMult
7.0, // fFatigueSwimRunBase 7.0f, // fFatigueSwimRunBase
0.0, // fFatigueSwimRunMult 0.0f, // fFatigueSwimRunMult
2.5, // fFatigueSwimWalkBase 2.5f, // fFatigueSwimWalkBase
0.0, // fFatigueSwimWalkMult 0.0f, // fFatigueSwimWalkMult
0.2, // fFightDispMult 0.2f, // fFightDispMult
0.005, // fFightDistanceMultiplier 0.005f, // fFightDistanceMultiplier
50.0, // fFightStealing 50.0f, // fFightStealing
3000.0, // fFleeDistance 3000.0f, // fFleeDistance
512.0, // fGreetDistanceReset 512.0f, // fGreetDistanceReset
0.1, // fHandtoHandHealthPer 0.1f, // fHandtoHandHealthPer
1.0, // fHandToHandReach 1.0f, // fHandToHandReach
0.5, // fHoldBreathEndMult 0.5f, // fHoldBreathEndMult
20.0, // fHoldBreathTime 20.0f, // fHoldBreathTime
0.75, // fIdleChanceMultiplier 0.75f, // fIdleChanceMultiplier
1.0, // fIngredientMult 1.0f, // fIngredientMult
0.5, // fInteriorHeadTrackMult 0.5f, // fInteriorHeadTrackMult
128.0, // fJumpAcrobaticsBase 128.0f, // fJumpAcrobaticsBase
4.0, // fJumpAcroMultiplier 4.0f, // fJumpAcroMultiplier
0.5, // fJumpEncumbranceBase 0.5f, // fJumpEncumbranceBase
1.0, // fJumpEncumbranceMultiplier 1.0f, // fJumpEncumbranceMultiplier
0.5, // fJumpMoveBase 0.5f, // fJumpMoveBase
0.5, // fJumpMoveMult 0.5f, // fJumpMoveMult
1.0, // fJumpRunMultiplier 1.0f, // fJumpRunMultiplier
0.5, // fKnockDownMult 0.5f, // fKnockDownMult
5.0, // fLevelMod 5.0f, // fLevelMod
0.1, // fLevelUpHealthEndMult 0.1f, // fLevelUpHealthEndMult
0.6, // fLightMaxMod 0.6f, // fLightMaxMod
10.0, // fLuckMod 10.0f, // fLuckMod
10.0, // fMagesGuildTravel 10.0f, // fMagesGuildTravel
1.5, // fMagicCreatureCastDelay 1.5f, // fMagicCreatureCastDelay
0.0167, // fMagicDetectRefreshRate 0.0167f, // fMagicDetectRefreshRate
1.0, // fMagicItemConstantMult 1.0f, // fMagicItemConstantMult
1.0, // fMagicItemCostMult 1.0f, // fMagicItemCostMult
1.0, // fMagicItemOnceMult 1.0f, // fMagicItemOnceMult
1.0, // fMagicItemPriceMult 1.0f, // fMagicItemPriceMult
0.05, // fMagicItemRechargePerSecond 0.05f, // fMagicItemRechargePerSecond
1.0, // fMagicItemStrikeMult 1.0f, // fMagicItemStrikeMult
1.0, // fMagicItemUsedMult 1.0f, // fMagicItemUsedMult
3.0, // fMagicStartIconBlink 3.0f, // fMagicStartIconBlink
0.5, // fMagicSunBlockedMult 0.5f, // fMagicSunBlockedMult
0.75, // fMajorSkillBonus 0.75f, // fMajorSkillBonus
300.0, // fMaxFlySpeed 300.0f, // fMaxFlySpeed
0.5, // fMaxHandToHandMult 0.5f, // fMaxHandToHandMult
400.0, // fMaxHeadTrackDistance 400.0f, // fMaxHeadTrackDistance
200.0, // fMaxWalkSpeed 200.0f, // fMaxWalkSpeed
300.0, // fMaxWalkSpeedCreature 300.0f, // fMaxWalkSpeedCreature
0.9, // fMedMaxMod 0.9f, // fMedMaxMod
0.1, // fMessageTimePerChar 0.1f, // fMessageTimePerChar
5.0, // fMinFlySpeed 5.0f, // fMinFlySpeed
0.1, // fMinHandToHandMult 0.1f, // fMinHandToHandMult
1.0, // fMinorSkillBonus 1.0f, // fMinorSkillBonus
100.0, // fMinWalkSpeed 100.0f, // fMinWalkSpeed
5.0, // fMinWalkSpeedCreature 5.0f, // fMinWalkSpeedCreature
1.25, // fMiscSkillBonus 1.25f, // fMiscSkillBonus
2.0, // fNPCbaseMagickaMult 2.0f, // fNPCbaseMagickaMult
0.5, // fNPCHealthBarFade 0.5f, // fNPCHealthBarFade
3.0, // fNPCHealthBarTime 3.0f, // fNPCHealthBarTime
1.0, // fPCbaseMagickaMult 1.0f, // fPCbaseMagickaMult
0.3, // fPerDieRollMult 0.3f, // fPerDieRollMult
5.0, // fPersonalityMod 5.0f, // fPersonalityMod
1.0, // fPerTempMult 1.0f, // fPerTempMult
-1.0, // fPickLockMult -1.0f, // fPickLockMult
0.3, // fPickPocketMod 0.3f, // fPickPocketMod
20.0, // fPotionMinUsefulDuration 20.0f, // fPotionMinUsefulDuration
0.5, // fPotionStrengthMult 0.5f, // fPotionStrengthMult
0.5, // fPotionT1DurMult 0.5f, // fPotionT1DurMult
1.5, // fPotionT1MagMult 1.5f, // fPotionT1MagMult
20.0, // fPotionT4BaseStrengthMult 20.0f, // fPotionT4BaseStrengthMult
12.0, // fPotionT4EquipStrengthMult 12.0f, // fPotionT4EquipStrengthMult
3000.0, // fProjectileMaxSpeed 3000.0f, // fProjectileMaxSpeed
400.0, // fProjectileMinSpeed 400.0f, // fProjectileMinSpeed
25.0, // fProjectileThrownStoreChance 25.0f, // fProjectileThrownStoreChance
3.0, // fRepairAmountMult 3.0f, // fRepairAmountMult
1.0, // fRepairMult 1.0f, // fRepairMult
1.0, // fReputationMod 1.0f, // fReputationMod
0.15, // fRestMagicMult 0.15f, // fRestMagicMult
0.0, // fSeriousWoundMult 0.0f, // fSeriousWoundMult
0.25, // fSleepRandMod 0.25f, // fSleepRandMod
0.3, // fSleepRestMod 0.3f, // fSleepRestMod
-1.0, // fSneakBootMult -1.0f, // fSneakBootMult
0.5, // fSneakDistanceBase 0.5f, // fSneakDistanceBase
0.002, // fSneakDistanceMultiplier 0.002f, // fSneakDistanceMultiplier
0.5, // fSneakNoViewMult 0.5f, // fSneakNoViewMult
1.0, // fSneakSkillMult 1.0f, // fSneakSkillMult
0.75, // fSneakSpeedMultiplier 0.75f, // fSneakSpeedMultiplier
1.0, // fSneakUseDelay 1.0f, // fSneakUseDelay
500.0, // fSneakUseDist 500.0f, // fSneakUseDist
1.5, // fSneakViewMult 1.5f, // fSneakViewMult
3.0, // fSoulGemMult 3.0f, // fSoulGemMult
0.8, // fSpecialSkillBonus 0.8f, // fSpecialSkillBonus
7.0, // fSpellMakingValueMult 7.0f, // fSpellMakingValueMult
2.0, // fSpellPriceMult 2.0f, // fSpellPriceMult
10.0, // fSpellValueMult 10.0f, // fSpellValueMult
0.25, // fStromWalkMult 0.25f, // fStromWalkMult
0.7, // fStromWindSpeed 0.7f, // fStromWindSpeed
3.0, // fSuffocationDamage 3.0f, // fSuffocationDamage
0.9, // fSwimHeightScale 0.9f, // fSwimHeightScale
0.1, // fSwimRunAthleticsMult 0.1f, // fSwimRunAthleticsMult
0.5, // fSwimRunBase 0.5f, // fSwimRunBase
0.02, // fSwimWalkAthleticsMult 0.02f, // fSwimWalkAthleticsMult
0.5, // fSwimWalkBase 0.5f, // fSwimWalkBase
1.0, // fSwingBlockBase 1.0f, // fSwingBlockBase
1.0, // fSwingBlockMult 1.0f, // fSwingBlockMult
1000.0, // fTargetSpellMaxSpeed 1000.0f, // fTargetSpellMaxSpeed
1000.0, // fThrownWeaponMaxSpeed 1000.0f, // fThrownWeaponMaxSpeed
300.0, // fThrownWeaponMinSpeed 300.0f, // fThrownWeaponMinSpeed
0.0, // fTrapCostMult 0.0f, // fTrapCostMult
4000.0, // fTravelMult 4000.0f, // fTravelMult
16000.0,// fTravelTimeMult 16000.0f,// fTravelTimeMult
0.1, // fUnarmoredBase1 0.1f, // fUnarmoredBase1
0.065, // fUnarmoredBase2 0.065f, // fUnarmoredBase2
30.0, // fVanityDelay 30.0f, // fVanityDelay
10.0, // fVoiceIdleOdds 10.0f, // fVoiceIdleOdds
0.0, // fWaterReflectUpdateAlways 0.0f, // fWaterReflectUpdateAlways
10.0, // fWaterReflectUpdateSeldom 10.0f, // fWaterReflectUpdateSeldom
0.1, // fWeaponDamageMult 0.1f, // fWeaponDamageMult
1.0, // fWeaponFatigueBlockMult 1.0f, // fWeaponFatigueBlockMult
0.25, // fWeaponFatigueMult 0.25f, // fWeaponFatigueMult
150.0, // fWereWolfAcrobatics 150.0f, // fWereWolfAcrobatics
150.0, // fWereWolfAgility 150.0f, // fWereWolfAgility
1.0, // fWereWolfAlchemy 1.0f, // fWereWolfAlchemy
1.0, // fWereWolfAlteration 1.0f, // fWereWolfAlteration
1.0, // fWereWolfArmorer 1.0f, // fWereWolfArmorer
150.0, // fWereWolfAthletics 150.0f, // fWereWolfAthletics
1.0, // fWereWolfAxe 1.0f, // fWereWolfAxe
1.0, // fWereWolfBlock 1.0f, // fWereWolfBlock
1.0, // fWereWolfBluntWeapon 1.0f, // fWereWolfBluntWeapon
1.0, // fWereWolfConjuration 1.0f, // fWereWolfConjuration
1.0, // fWereWolfDestruction 1.0f, // fWereWolfDestruction
1.0, // fWereWolfEnchant 1.0f, // fWereWolfEnchant
150.0, // fWereWolfEndurance 150.0f, // fWereWolfEndurance
400.0, // fWereWolfFatigue 400.0f, // fWereWolfFatigue
100.0, // fWereWolfHandtoHand 100.0f, // fWereWolfHandtoHand
2.0, // fWereWolfHealth 2.0f, // fWereWolfHealth
1.0, // fWereWolfHeavyArmor 1.0f, // fWereWolfHeavyArmor
1.0, // fWereWolfIllusion 1.0f, // fWereWolfIllusion
1.0, // fWereWolfIntellegence 1.0f, // fWereWolfIntellegence
1.0, // fWereWolfLightArmor 1.0f, // fWereWolfLightArmor
1.0, // fWereWolfLongBlade 1.0f, // fWereWolfLongBlade
1.0, // fWereWolfLuck 1.0f, // fWereWolfLuck
100.0, // fWereWolfMagicka 100.0f, // fWereWolfMagicka
1.0, // fWereWolfMarksman 1.0f, // fWereWolfMarksman
1.0, // fWereWolfMediumArmor 1.0f, // fWereWolfMediumArmor
1.0, // fWereWolfMerchantile 1.0f, // fWereWolfMerchantile
1.0, // fWereWolfMysticism 1.0f, // fWereWolfMysticism
1.0, // fWereWolfPersonality 1.0f, // fWereWolfPersonality
1.0, // fWereWolfRestoration 1.0f, // fWereWolfRestoration
1.5, // fWereWolfRunMult 1.5f, // fWereWolfRunMult
1.0, // fWereWolfSecurity 1.0f, // fWereWolfSecurity
1.0, // fWereWolfShortBlade 1.0f, // fWereWolfShortBlade
1.5, // fWereWolfSilverWeaponDamageMult 1.5f, // fWereWolfSilverWeaponDamageMult
1.0, // fWereWolfSneak 1.0f, // fWereWolfSneak
1.0, // fWereWolfSpear 1.0f, // fWereWolfSpear
1.0, // fWereWolfSpeechcraft 1.0f, // fWereWolfSpeechcraft
150.0, // fWereWolfSpeed 150.0f, // fWereWolfSpeed
150.0, // fWereWolfStrength 150.0f, // fWereWolfStrength
100.0, // fWereWolfUnarmored 100.0f, // fWereWolfUnarmored
1.0, // fWereWolfWillPower 1.0f, // fWereWolfWillPower
15.0 // fWortChanceValue 15.0f // fWortChanceValue
}; };
const int CSMWorld::DefaultGmsts::IntsDefaultValues[CSMWorld::DefaultGmsts::IntCount] = const int CSMWorld::DefaultGmsts::IntsDefaultValues[CSMWorld::DefaultGmsts::IntCount] =

@ -191,7 +191,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco
if (index==-1) if (index==-1)
{ {
int index = mIdCollection->getAppendIndex (id, type); index = mIdCollection->getAppendIndex (id, type);
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);

@ -198,12 +198,12 @@ QModelIndex CSMWorld::IdTree::parent (const QModelIndex& index) const
return QModelIndex(); return QModelIndex();
unsigned int id = index.internalId(); unsigned int id = index.internalId();
const std::pair<int, int>& adress(unfoldIndexAddress(id)); const std::pair<int, int>& address(unfoldIndexAddress(id));
if (adress.first >= this->rowCount() || adress.second >= this->columnCount()) if (address.first >= this->rowCount() || address.second >= this->columnCount())
throw "Parent index is not present in the model"; throw "Parent index is not present in the model";
return createIndex(adress.first, adress.second); return createIndex(address.first, address.second);
} }
unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const

@ -63,7 +63,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
std::cerr << "Position: #" << index.first << " " << index.second std::cerr << "Position: #" << index.first << " " << index.second
<<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl;
std::ostringstream stream; stream.clear();
stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1];
ref.mCell = stream.str(); // overwrite ref.mCell = stream.str(); // overwrite
} }

@ -50,7 +50,7 @@ namespace CSVRender
updateCellData(mData.getCells().getRecord(cellIndex)); updateCellData(mData.getCells().getRecord(cellIndex));
} }
// Keep water existance/height up to date // Keep water existence/height up to date
QAbstractItemModel* cells = mData.getTableModel(CSMWorld::UniversalId::Type_Cells); QAbstractItemModel* cells = mData.getTableModel(CSMWorld::UniversalId::Type_Cells);
connect(cells, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), connect(cells, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
this, SLOT(cellDataChanged(const QModelIndex&, const QModelIndex&))); this, SLOT(cellDataChanged(const QModelIndex&, const QModelIndex&)));

@ -516,7 +516,7 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint)
// Current coordinate // Current coordinate
int x, y; int x, y;
// Loop throught all the coordinates to add them to selection // Loop through all the coordinates to add them to selection
while (stream >> ignore1 >> ignore2 >> x >> y) while (stream >> ignore1 >> ignore2 >> x >> y)
selection.add (CSMWorld::CellCoordinates (x, y)); selection.add (CSMWorld::CellCoordinates (x, y));

@ -214,7 +214,7 @@ SceneWidget::SceneWidget(boost::shared_ptr<Resource::ResourceSystem> resourceSys
SceneWidget::~SceneWidget() SceneWidget::~SceneWidget()
{ {
// Since we're holding on to the scene templates past the existance of this graphics context, we'll need to manually release the created objects // Since we're holding on to the scene templates past the existence of this graphics context, we'll need to manually release the created objects
mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
} }

@ -45,8 +45,8 @@ namespace CSVWorld
///< \note Non-existent cells are not listed. ///< \note Non-existent cells are not listed.
QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const; QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const;
///< \param existant Include existant cells. ///< \param existent Include existent cells.
/// \param nonExistant Include non-existant cells. /// \param nonExistent Include non-existent cells.
QModelIndexList getMissingRegionCells() const; QModelIndexList getMissingRegionCells() const;
///< Unselected cells within all regions that have at least one selected cell. ///< Unselected cells within all regions that have at least one selected cell.

@ -42,7 +42,7 @@ add_openmw_dir (mwgui
itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
draganddrop timeadvancer jailscreen draganddrop timeadvancer jailscreen itemchargeview
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue
@ -179,6 +179,21 @@ target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT})
endif() endif()
if(APPLE) if(APPLE)
set(BUNDLE_RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources")
set(OPENMW_MYGUI_FILES_ROOT ${BUNDLE_RESOURCES_DIR})
set(OPENMW_SHADERS_ROOT ${BUNDLE_RESOURCES_DIR})
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)
configure_file("${OpenMW_BINARY_DIR}/settings-default.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY)
configure_file("${OpenMW_BINARY_DIR}/openmw.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY)
configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY)
add_custom_command(TARGET openmw
POST_BUILD
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${BUNDLE_RESOURCES_DIR}/resources")
find_library(COCOA_FRAMEWORK Cocoa) find_library(COCOA_FRAMEWORK Cocoa)
find_library(IOKIT_FRAMEWORK IOKit) find_library(IOKIT_FRAMEWORK IOKit)
target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK})
@ -186,7 +201,7 @@ if(APPLE)
if (FFmpeg_FOUND) if (FFmpeg_FOUND)
find_library(COREVIDEO_FRAMEWORK CoreVideo) find_library(COREVIDEO_FRAMEWORK CoreVideo)
find_library(VDA_FRAMEWORK VideoDecodeAcceleration) find_library(VDA_FRAMEWORK VideoDecodeAcceleration)
target_link_libraries(openmw ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK}) target_link_libraries(openmw z ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK})
endif() endif()
endif(APPLE) endif(APPLE)
@ -202,3 +217,8 @@ if (MSVC)
endif (CMAKE_CL_64) endif (CMAKE_CL_64)
add_definitions("-D_USE_MATH_DEFINES") add_definitions("-D_USE_MATH_DEFINES")
endif (MSVC) endif (MSVC)
if (WIN32)
INSTALL(TARGETS openmw RUNTIME DESTINATION ".")
endif (WIN32)

@ -493,7 +493,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
// Create sound system // Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound)); mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mFallbackMap, mUseSound));
if (!mSkipMenu) if (!mSkipMenu)
{ {

@ -352,9 +352,8 @@ int main(int argc, char**argv)
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
// FIXME: set current dir to bundle path boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
//boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path(); boost::filesystem::current_path(binary_path.parent_path());
//boost::filesystem::current_path(bundlePath);
#endif #endif
engine.reset(new OMW::Engine(cfgMgr)); engine.reset(new OMW::Engine(cfgMgr));

@ -5,6 +5,19 @@
#include <set> #include <set>
#include <vector> #include <vector>
#include <stdint.h>
namespace Loading
{
class Listener;
}
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace MWBase namespace MWBase
{ {
/// \brief Interface for input manager (implemented in MWInput) /// \brief Interface for input manager (implemented in MWInput)
@ -56,6 +69,10 @@ namespace MWBase
/// Returns if the last used input device was a joystick or a keyboard /// Returns if the last used input device was a joystick or a keyboard
/// @return true if joystick, false otherwise /// @return true if joystick, false otherwise
virtual bool joystickLastUsed() = 0; virtual bool joystickLastUsed() = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0;
virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0;
}; };
} }

@ -233,7 +233,7 @@ namespace MWBase
virtual void removeDialog(MWGui::Layout* dialog) = 0; virtual void removeDialog(MWGui::Layout* dialog) = 0;
///Gracefully attempts to exit the topmost GUI mode ///Gracefully attempts to exit the topmost GUI mode
/** No guarentee of actually closing the window **/ /** No guarantee of actually closing the window **/
virtual void exitCurrentGuiMode() = 0; virtual void exitCurrentGuiMode() = 0;
virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0;

@ -548,7 +548,7 @@ 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::ConstPtr& actor) = 0; virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor) const = 0;
/// Return a vector aiming the actor's weapon towards a target. /// Return a vector aiming the actor's weapon towards a target.
/// @note The length of the vector is the distance between actor and target. /// @note The length of the vector is the distance between actor and target.
@ -560,6 +560,12 @@ namespace MWBase
virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0; virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0;
virtual bool isPlayerInJail() const = 0; virtual bool isPlayerInJail() const = 0;
/// Return terrain height at \a worldPos position.
virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const = 0;
/// Return physical or rendering half extents of the given actor.
virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const = 0;
}; };
} }

@ -118,9 +118,9 @@ namespace MWClass
} }
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::ManualRef ref(store, id); MWWorld::ManualRef manualRef(store, id);
ref.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition()); manualRef.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition());
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition()); MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(manualRef.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition());
customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId();
customData.mSpawn = false; customData.mSpawn = false;
} }

@ -174,7 +174,7 @@ namespace MWDialogue
executeScript (info->mResultScript); executeScript (info->mResultScript);
mLastTopic = Misc::StringUtils::lowerCase(it->mId); mLastTopic = Misc::StringUtils::lowerCase(it->mId);
// update topics again to accomodate changes resulting from executeScript // update topics again to accommodate changes resulting from executeScript
updateTopics(); updateTopics();
return; return;

@ -200,15 +200,15 @@ namespace MWGui
std::set<MWMechanics::EffectKey> effectIds = mAlchemy->listEffects(); std::set<MWMechanics::EffectKey> effectIds = mAlchemy->listEffects();
Widgets::SpellEffectList list; Widgets::SpellEffectList list;
unsigned int effectIndex=0; unsigned int effectIndex=0;
for (std::set<MWMechanics::EffectKey>::iterator it = effectIds.begin(); it != effectIds.end(); ++it) for (std::set<MWMechanics::EffectKey>::iterator it2 = effectIds.begin(); it2 != effectIds.end(); ++it2)
{ {
Widgets::SpellEffectParams params; Widgets::SpellEffectParams params;
params.mEffectID = it->mId; params.mEffectID = it2->mId;
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(it->mId); const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(it2->mId);
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
params.mSkill = it->mArg; params.mSkill = it2->mArg;
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
params.mAttribute = it->mArg; params.mAttribute = it2->mArg;
params.mIsConstant = true; params.mIsConstant = true;
params.mNoTarget = true; params.mNoTarget = true;

@ -226,7 +226,7 @@ namespace MWGui
coord.top += lineHeight; coord.top += lineHeight;
end = categories[category].spells.end(); end = categories[category].spells.end();
for (std::vector<std::string>::const_iterator it = categories[category].spells.begin(); it != end; ++it) for (it = categories[category].spells.begin(); it != end; ++it)
{ {
const std::string &spellId = *it; const std::string &spellId = *it;
spellWidget = mSpellArea->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + MyGUI::utility::toString(i)); spellWidget = mSpellArea->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + MyGUI::utility::toString(i));

@ -132,6 +132,12 @@ namespace MWGui
mReviewDialog->configureSkills(major, minor); mReviewDialog->configureSkills(major, minor);
} }
void CharacterCreation::onFrame(float duration)
{
if (mReviewDialog)
mReviewDialog->onFrame(duration);
}
void CharacterCreation::spawnDialog(const char id) void CharacterCreation::spawnDialog(const char id)
{ {
try try

@ -50,6 +50,8 @@ namespace MWGui
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value);
void configureSkills (const SkillList& major, const SkillList& minor); void configureSkills (const SkillList& major, const SkillList& minor);
void onFrame(float duration);
private: private:
osg::Group* mParent; osg::Group* mParent;
Resource::ResourceSystem* mResourceSystem; Resource::ResourceSystem* mResourceSystem;

@ -85,8 +85,8 @@ void InventoryItemModel::update()
if (mActor.getClass().hasInventoryStore(mActor)) if (mActor.getClass().hasInventoryStore(mActor))
{ {
MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor); MWWorld::InventoryStore& invStore = mActor.getClass().getInventoryStore(mActor);
if (store.isEquipped(newItem.mBase)) if (invStore.isEquipped(newItem.mBase))
newItem.mType = ItemStack::Type_Equipped; newItem.mType = ItemStack::Type_Equipped;
} }

@ -0,0 +1,212 @@
#include "itemchargeview.hpp"
#include <set>
#include <MyGUI_Gui.h>
#include <MyGUI_TextBox.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_FactoryManager.h>
#include <components/esm/loadench.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "itemmodel.hpp"
#include "itemwidget.hpp"
namespace MWGui
{
ItemChargeView::ItemChargeView()
: mScrollView(NULL),
mDisplayMode(DisplayMode_Health)
{
}
void ItemChargeView::registerComponents()
{
MyGUI::FactoryManager::getInstance().registerFactory<ItemChargeView>("Widget");
}
void ItemChargeView::initialiseOverride()
{
Base::initialiseOverride();
assignWidget(mScrollView, "ScrollView");
if (mScrollView == NULL)
throw std::runtime_error("Item charge view needs a scroll view");
mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top);
}
void ItemChargeView::setModel(ItemModel* model)
{
mModel.reset(model);
}
void ItemChargeView::setDisplayMode(ItemChargeView::DisplayMode type)
{
mDisplayMode = type;
update();
}
void ItemChargeView::update()
{
if (!mModel.get())
{
layoutWidgets();
return;
}
mModel->update();
Lines lines;
std::set<Lines::const_iterator> visitedLines;
for (size_t i = 0; i < mModel->getItemCount(); ++i)
{
ItemStack stack = mModel->getItem(static_cast<ItemModel::ModelIndex>(i));
bool found = false;
for (Lines::const_iterator iter = mLines.begin(); iter != mLines.end(); ++iter)
{
if (iter->mItemPtr == stack.mBase)
{
found = true;
visitedLines.insert(iter);
// update line widgets
updateLine(*iter);
lines.push_back(*iter);
break;
}
}
if (!found)
{
// add line widgets
Line line;
line.mItemPtr = stack.mBase;
line.mText = mScrollView->createWidget<MyGUI::TextBox>("SandText", MyGUI::IntCoord(), MyGUI::Align::Default);
line.mText->setNeedMouseFocus(false);
line.mIcon = mScrollView->createWidget<ItemWidget>("MW_ItemIconSmall", MyGUI::IntCoord(), MyGUI::Align::Default);
line.mIcon->setItem(line.mItemPtr);
line.mIcon->setUserString("ToolTipType", "ItemPtr");
line.mIcon->setUserData(line.mItemPtr);
line.mIcon->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemChargeView::onIconClicked);
line.mIcon->eventMouseWheel += MyGUI::newDelegate(this, &ItemChargeView::onMouseWheelMoved);
line.mCharge = mScrollView->createWidget<Widgets::MWDynamicStat>("MW_ChargeBar", MyGUI::IntCoord(), MyGUI::Align::Default);
line.mCharge->setNeedMouseFocus(false);
updateLine(line);
lines.push_back(line);
}
}
for (Lines::iterator iter = mLines.begin(); iter != mLines.end(); ++iter)
{
if (visitedLines.count(iter))
continue;
// remove line widgets
MyGUI::Gui::getInstance().destroyWidget(iter->mText);
MyGUI::Gui::getInstance().destroyWidget(iter->mIcon);
MyGUI::Gui::getInstance().destroyWidget(iter->mCharge);
}
mLines.swap(lines);
layoutWidgets();
}
void ItemChargeView::layoutWidgets()
{
int currentY = 0;
for (Lines::const_iterator iter = mLines.begin(); iter != mLines.end(); ++iter)
{
iter->mText->setCoord(8, currentY, mScrollView->getWidth()-8, 18);
currentY += 19;
iter->mIcon->setCoord(16, currentY, 32, 32);
iter->mCharge->setCoord(72, currentY+2, std::max(199, mScrollView->getWidth()-72-38), 20);
currentY += 32 + 4;
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
mScrollView->setVisibleVScroll(false);
mScrollView->setCanvasSize(MyGUI::IntSize(mScrollView->getWidth(), std::max(mScrollView->getHeight(), currentY)));
mScrollView->setVisibleVScroll(true);
}
void ItemChargeView::resetScrollbars()
{
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
}
void ItemChargeView::setSize(const MyGUI::IntSize& value)
{
bool changed = (value.width != getWidth() || value.height != getHeight());
Base::setSize(value);
if (changed)
layoutWidgets();
}
void ItemChargeView::setCoord(const MyGUI::IntCoord& value)
{
bool changed = (value.width != getWidth() || value.height != getHeight());
Base::setCoord(value);
if (changed)
layoutWidgets();
}
void ItemChargeView::updateLine(const ItemChargeView::Line& line)
{
line.mText->setCaption(line.mItemPtr.getClass().getName(line.mItemPtr));
line.mCharge->setVisible(false);
switch (mDisplayMode)
{
case DisplayMode_Health:
if (!line.mItemPtr.getClass().hasItemHealth(line.mItemPtr))
break;
line.mCharge->setVisible(true);
line.mCharge->setValue(line.mItemPtr.getClass().getItemHealth(line.mItemPtr),
line.mItemPtr.getClass().getItemMaxHealth(line.mItemPtr));
break;
case DisplayMode_EnchantmentCharge:
std::string enchId = line.mItemPtr.getClass().getEnchantment(line.mItemPtr);
if (enchId.empty())
break;
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchId);
if (!ench)
break;
line.mCharge->setVisible(true);
line.mCharge->setValue(static_cast<int>(line.mItemPtr.getCellRef().getEnchantmentCharge()),
ench->mData.mCharge);
break;
}
}
void ItemChargeView::onIconClicked(MyGUI::Widget* sender)
{
eventItemClicked(this, *sender->getUserData<MWWorld::Ptr>());
}
void ItemChargeView::onMouseWheelMoved(MyGUI::Widget* /*sender*/, int rel)
{
if (mScrollView->getViewOffset().top + rel*0.3f > 0)
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
else
mScrollView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mScrollView->getViewOffset().top + rel*0.3f)));
}
}

@ -0,0 +1,78 @@
#ifndef MWGUI_ITEMCHARGEVIEW_H
#define MWGUI_ITEMCHARGEVIEW_H
#include <vector>
#include <memory>
#include <MyGUI_Widget.h>
#include "../mwworld/ptr.hpp"
#include "widgets.hpp"
namespace MyGUI
{
class TextBox;
class ScrollView;
}
namespace MWGui
{
class ItemModel;
class ItemWidget;
class ItemChargeView : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(ItemChargeView)
public:
enum DisplayMode
{
DisplayMode_Health,
DisplayMode_EnchantmentCharge
};
ItemChargeView();
/// Register needed components with MyGUI's factory manager
static void registerComponents();
virtual void initialiseOverride();
/// Takes ownership of \a model
void setModel(ItemModel* model);
void setDisplayMode(DisplayMode type);
void update();
void layoutWidgets();
void resetScrollbars();
virtual void setSize(const MyGUI::IntSize& value);
virtual void setCoord(const MyGUI::IntCoord& value);
MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, const MWWorld::Ptr&> eventItemClicked;
private:
struct Line
{
MWWorld::Ptr mItemPtr;
MyGUI::TextBox* mText;
ItemWidget* mIcon;
Widgets::MWDynamicStatPtr mCharge;
};
void updateLine(const Line& line);
void onIconClicked(MyGUI::Widget* sender);
void onMouseWheelMoved(MyGUI::Widget* sender, int rel);
typedef std::vector<Line> Lines;
Lines mLines;
std::auto_ptr<ItemModel> mModel;
MyGUI::ScrollView* mScrollView;
DisplayMode mDisplayMode;
};
}
#endif

@ -101,6 +101,8 @@ namespace MWGui
{ {
if (mFrame) if (mFrame)
mFrame->setImageTexture(""); mFrame->setImageTexture("");
if (mItemShadow)
mItemShadow->setImageTexture("");
mItem->setImageTexture(""); mItem->setImageTexture("");
mText->setCaption(""); mText->setCaption("");
return; return;

@ -5,6 +5,8 @@
#include <MyGUI_ScrollView.h> #include <MyGUI_ScrollView.h>
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <components/widgets/box.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -21,6 +23,9 @@
#include "widgets.hpp" #include "widgets.hpp"
#include "itemwidget.hpp" #include "itemwidget.hpp"
#include "itemchargeview.hpp"
#include "sortfilteritemmodel.hpp"
#include "inventoryitemmodel.hpp"
namespace MWGui namespace MWGui
{ {
@ -29,13 +34,15 @@ Recharge::Recharge()
: WindowBase("openmw_recharge_dialog.layout") : WindowBase("openmw_recharge_dialog.layout")
{ {
getWidget(mBox, "Box"); getWidget(mBox, "Box");
getWidget(mView, "View");
getWidget(mGemBox, "GemBox"); getWidget(mGemBox, "GemBox");
getWidget(mGemIcon, "GemIcon"); getWidget(mGemIcon, "GemIcon");
getWidget(mChargeLabel, "ChargeLabel"); getWidget(mChargeLabel, "ChargeLabel");
getWidget(mCancelButton, "CancelButton"); getWidget(mCancelButton, "CancelButton");
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onCancel); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onCancel);
mBox->eventItemClicked += MyGUI::newDelegate(this, &Recharge::onItemClicked);
mBox->setDisplayMode(ItemChargeView::DisplayMode_EnchantmentCharge);
setVisible(false); setVisible(false);
} }
@ -43,8 +50,13 @@ Recharge::Recharge()
void Recharge::open() void Recharge::open()
{ {
center(); center();
SortFilterItemModel * model = new SortFilterItemModel(new InventoryItemModel(MWMechanics::getPlayer()));
model->setFilter(SortFilterItemModel::Filter_OnlyRechargable);
mBox->setModel(model);
// Reset scrollbars // Reset scrollbars
mView->setViewOffset(MyGUI::IntPoint(0, 0)); mBox->resetScrollbars();
} }
void Recharge::exit() void Recharge::exit()
@ -72,66 +84,16 @@ void Recharge::updateView()
bool toolBoxVisible = (gem.getRefData().getCount() != 0); bool toolBoxVisible = (gem.getRefData().getCount() != 0);
mGemBox->setVisible(toolBoxVisible); mGemBox->setVisible(toolBoxVisible);
mGemBox->setUserString("Hidden", toolBoxVisible ? "false" : "true");
bool toolBoxWasVisible = (mBox->getPosition().top != mGemBox->getPosition().top); mBox->update();
if (toolBoxVisible && !toolBoxWasVisible)
{
// shrink
mBox->setPosition(mBox->getPosition() + MyGUI::IntPoint(0, mGemBox->getSize().height));
mBox->setSize(mBox->getSize() - MyGUI::IntSize(0,mGemBox->getSize().height));
}
else if (!toolBoxVisible && toolBoxWasVisible)
{
// expand
mBox->setPosition(MyGUI::IntPoint (mBox->getPosition().left, mGemBox->getPosition().top));
mBox->setSize(mBox->getSize() + MyGUI::IntSize(0,mGemBox->getSize().height));
}
while (mView->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mView->getChildAt(0));
int currentY = 0; Gui::Box* box = dynamic_cast<Gui::Box*>(mMainWidget);
if (box == NULL)
throw std::runtime_error("main widget must be a box");
MWWorld::Ptr player = MWMechanics::getPlayer(); box->notifyChildrenSizeChanged();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); center();
for (MWWorld::ContainerStoreIterator iter (store.begin());
iter!=store.end(); ++iter)
{
std::string enchantmentName = iter->getClass().getEnchantment(*iter);
if (enchantmentName.empty())
continue;
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchantmentName);
if (iter->getCellRef().getEnchantmentCharge() >= enchantment->mData.mCharge
|| iter->getCellRef().getEnchantmentCharge() == -1)
continue;
MyGUI::TextBox* text = mView->createWidget<MyGUI::TextBox> (
"SandText", MyGUI::IntCoord(8, currentY, mView->getWidth()-8, 18), MyGUI::Align::Default);
text->setCaption(iter->getClass().getName(*iter));
text->setNeedMouseFocus(false);
currentY += 19;
ItemWidget* icon = mView->createWidget<ItemWidget> (
"MW_ItemIconSmall", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default);
icon->setItem(*iter);
icon->setUserString("ToolTipType", "ItemPtr");
icon->setUserData(*iter);
icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onItemClicked);
icon->eventMouseWheel += MyGUI::newDelegate(this, &Recharge::onMouseWheel);
Widgets::MWDynamicStatPtr chargeWidget = mView->createWidget<Widgets::MWDynamicStat>
("MW_ChargeBar", MyGUI::IntCoord(72, currentY+2, 199, 20), MyGUI::Align::Default);
chargeWidget->setValue(static_cast<int>(iter->getCellRef().getEnchantmentCharge()), enchantment->mData.mCharge);
chargeWidget->setNeedMouseFocus(false);
currentY += 32 + 4;
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
mView->setVisibleVScroll(false);
mView->setCanvasSize (MyGUI::IntSize(mView->getWidth(), std::max(mView->getHeight(), currentY)));
mView->setVisibleVScroll(true);
} }
void Recharge::onCancel(MyGUI::Widget *sender) void Recharge::onCancel(MyGUI::Widget *sender)
@ -139,15 +101,13 @@ void Recharge::onCancel(MyGUI::Widget *sender)
exit(); exit();
} }
void Recharge::onItemClicked(MyGUI::Widget *sender) void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
{ {
MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>(); MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();
if (!gem.getRefData().getCount()) if (!gem.getRefData().getCount())
return; return;
MWWorld::Ptr item = *sender->getUserData<MWWorld::Ptr>();
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player);
@ -198,12 +158,4 @@ void Recharge::onItemClicked(MyGUI::Widget *sender)
updateView(); updateView();
} }
void Recharge::onMouseWheel(MyGUI::Widget* _sender, int _rel)
{
if (mView->getViewOffset().top + _rel*0.3f > 0)
mView->setViewOffset(MyGUI::IntPoint(0, 0));
else
mView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mView->getViewOffset().top + _rel*0.3f)));
}
} }

@ -12,6 +12,7 @@ namespace MWGui
{ {
class ItemWidget; class ItemWidget;
class ItemChargeView;
class Recharge : public WindowBase class Recharge : public WindowBase
{ {
@ -25,8 +26,7 @@ public:
void start (const MWWorld::Ptr& gem); void start (const MWWorld::Ptr& gem);
protected: protected:
MyGUI::Widget* mBox; ItemChargeView* mBox;
MyGUI::ScrollView* mView;
MyGUI::Widget* mGemBox; MyGUI::Widget* mGemBox;
@ -38,7 +38,7 @@ protected:
void updateView(); void updateView();
void onItemClicked (MyGUI::Widget* sender); void onItemClicked (MyGUI::Widget* sender, const MWWorld::Ptr& item);
void onCancel (MyGUI::Widget* sender); void onCancel (MyGUI::Widget* sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onMouseWheel(MyGUI::Widget* _sender, int _rel);

@ -4,6 +4,9 @@
#include <MyGUI_ScrollView.h> #include <MyGUI_ScrollView.h>
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <MyGUI_ItemBox.h>
#include <components/widgets/box.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -17,6 +20,9 @@
#include "widgets.hpp" #include "widgets.hpp"
#include "itemwidget.hpp" #include "itemwidget.hpp"
#include "itemchargeview.hpp"
#include "sortfilteritemmodel.hpp"
#include "inventoryitemmodel.hpp"
namespace MWGui namespace MWGui
{ {
@ -25,7 +31,6 @@ Repair::Repair()
: WindowBase("openmw_repair.layout") : WindowBase("openmw_repair.layout")
{ {
getWidget(mRepairBox, "RepairBox"); getWidget(mRepairBox, "RepairBox");
getWidget(mRepairView, "RepairView");
getWidget(mToolBox, "ToolBox"); getWidget(mToolBox, "ToolBox");
getWidget(mToolIcon, "ToolIcon"); getWidget(mToolIcon, "ToolIcon");
getWidget(mUsesLabel, "UsesLabel"); getWidget(mUsesLabel, "UsesLabel");
@ -33,13 +38,21 @@ Repair::Repair()
getWidget(mCancelButton, "CancelButton"); getWidget(mCancelButton, "CancelButton");
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel);
mRepairBox->eventItemClicked += MyGUI::newDelegate(this, &Repair::onRepairItem);
mRepairBox->setDisplayMode(ItemChargeView::DisplayMode_Health);
} }
void Repair::open() void Repair::open()
{ {
center(); center();
SortFilterItemModel * model = new SortFilterItemModel(new InventoryItemModel(MWMechanics::getPlayer()));
model->setFilter(SortFilterItemModel::Filter_OnlyRepairable);
mRepairBox->setModel(model);
// Reset scrollbars // Reset scrollbars
mRepairView->setViewOffset(MyGUI::IntPoint(0, 0)); mRepairBox->resetScrollbars();
} }
void Repair::exit() void Repair::exit()
@ -75,89 +88,31 @@ void Repair::updateRepairView()
bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0); bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0);
mToolBox->setVisible(toolBoxVisible); mToolBox->setVisible(toolBoxVisible);
mToolBox->setUserString("Hidden", toolBoxVisible ? "false" : "true");
bool toolBoxWasVisible = (mRepairBox->getPosition().top != mToolBox->getPosition().top); mRepairBox->update();
if (toolBoxVisible && !toolBoxWasVisible)
{
// shrink
mRepairBox->setPosition(mRepairBox->getPosition() + MyGUI::IntPoint(0,mToolBox->getSize().height));
mRepairBox->setSize(mRepairBox->getSize() - MyGUI::IntSize(0,mToolBox->getSize().height));
}
else if (!toolBoxVisible && toolBoxWasVisible)
{
// expand
mRepairBox->setPosition(MyGUI::IntPoint (mRepairBox->getPosition().left, mToolBox->getPosition().top));
mRepairBox->setSize(mRepairBox->getSize() + MyGUI::IntSize(0,mToolBox->getSize().height));
}
while (mRepairView->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mRepairView->getChildAt(0));
int currentY = 0; Gui::Box* box = dynamic_cast<Gui::Box*>(mMainWidget);
if (box == NULL)
throw std::runtime_error("main widget must be a box");
MWWorld::Ptr player = MWMechanics::getPlayer(); box->notifyChildrenSizeChanged();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); center();
int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor;
for (MWWorld::ContainerStoreIterator iter (store.begin(categories));
iter!=store.end(); ++iter)
{
if (iter->getClass().hasItemHealth(*iter))
{
int maxDurability = iter->getClass().getItemMaxHealth(*iter);
int durability = iter->getClass().getItemHealth(*iter);
if (maxDurability == durability)
continue;
MyGUI::TextBox* text = mRepairView->createWidget<MyGUI::TextBox> (
"SandText", MyGUI::IntCoord(8, currentY, mRepairView->getWidth()-8, 18), MyGUI::Align::Default);
text->setCaption(iter->getClass().getName(*iter));
text->setNeedMouseFocus(false);
currentY += 19;
ItemWidget* icon = mRepairView->createWidget<ItemWidget> (
"MW_ItemIconSmall", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default);
icon->setItem(*iter);
icon->setUserString("ToolTipType", "ItemPtr");
icon->setUserData(*iter);
icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onRepairItem);
icon->eventMouseWheel += MyGUI::newDelegate(this, &Repair::onMouseWheel);
Widgets::MWDynamicStatPtr chargeWidget = mRepairView->createWidget<Widgets::MWDynamicStat>
("MW_ChargeBar", MyGUI::IntCoord(72, currentY+2, 199, 20), MyGUI::Align::Default);
chargeWidget->setValue(durability, maxDurability);
chargeWidget->setNeedMouseFocus(false);
currentY += 32 + 4;
}
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
mRepairView->setVisibleVScroll(false);
mRepairView->setCanvasSize (MyGUI::IntSize(mRepairView->getWidth(), std::max(mRepairView->getHeight(), currentY)));
mRepairView->setVisibleVScroll(true);
} }
void Repair::onCancel(MyGUI::Widget *sender) void Repair::onCancel(MyGUI::Widget* /*sender*/)
{ {
exit(); exit();
} }
void Repair::onRepairItem(MyGUI::Widget *sender) void Repair::onRepairItem(MyGUI::Widget* /*sender*/, const MWWorld::Ptr& ptr)
{ {
if (!mRepair.getTool().getRefData().getCount()) if (!mRepair.getTool().getRefData().getCount())
return; return;
mRepair.repair(*sender->getUserData<MWWorld::Ptr>()); mRepair.repair(ptr);
updateRepairView(); updateRepairView();
} }
void Repair::onMouseWheel(MyGUI::Widget* _sender, int _rel)
{
if (mRepairView->getViewOffset().top + _rel*0.3f > 0)
mRepairView->setViewOffset(MyGUI::IntPoint(0, 0));
else
mRepairView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mRepairView->getViewOffset().top + _rel*0.3f)));
}
} }

@ -9,6 +9,7 @@ namespace MWGui
{ {
class ItemWidget; class ItemWidget;
class ItemChargeView;
class Repair : public WindowBase class Repair : public WindowBase
{ {
@ -22,8 +23,7 @@ public:
void startRepairItem (const MWWorld::Ptr& item); void startRepairItem (const MWWorld::Ptr& item);
protected: protected:
MyGUI::Widget* mRepairBox; ItemChargeView* mRepairBox;
MyGUI::ScrollView* mRepairView;
MyGUI::Widget* mToolBox; MyGUI::Widget* mToolBox;
@ -38,9 +38,8 @@ protected:
void updateRepairView(); void updateRepairView();
void onRepairItem (MyGUI::Widget* sender); void onRepairItem(MyGUI::Widget* sender, const MWWorld::Ptr& ptr);
void onCancel(MyGUI::Widget* sender); void onCancel(MyGUI::Widget* sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
}; };

@ -10,6 +10,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwmechanics/autocalcspell.hpp"
#include "tooltips.hpp" #include "tooltips.hpp"
@ -29,7 +30,8 @@ namespace MWGui
const int ReviewDialog::sLineHeight = 18; const int ReviewDialog::sLineHeight = 18;
ReviewDialog::ReviewDialog() ReviewDialog::ReviewDialog()
: WindowModal("openmw_chargen_review.layout") : WindowModal("openmw_chargen_review.layout"),
mUpdateSkillArea(false)
{ {
// Centre dialog // Centre dialog
center(); center();
@ -102,7 +104,16 @@ namespace MWGui
void ReviewDialog::open() void ReviewDialog::open()
{ {
WindowModal::open(); WindowModal::open();
mUpdateSkillArea = true;
}
void ReviewDialog::onFrame(float /*duration*/)
{
if (mUpdateSkillArea)
{
updateSkillArea(); updateSkillArea();
mUpdateSkillArea = false;
}
} }
void ReviewDialog::setPlayerName(const std::string &name) void ReviewDialog::setPlayerName(const std::string &name)
@ -121,6 +132,8 @@ namespace MWGui
ToolTips::createRaceToolTip(mRaceWidget, race); ToolTips::createRaceToolTip(mRaceWidget, race);
mRaceWidget->setCaption(race->mName); mRaceWidget->setCaption(race->mName);
} }
mUpdateSkillArea = true;
} }
void ReviewDialog::setClass(const ESM::Class& class_) void ReviewDialog::setClass(const ESM::Class& class_)
@ -141,6 +154,8 @@ namespace MWGui
mBirthSignWidget->setCaption(sign->mName); mBirthSignWidget->setCaption(sign->mName);
ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId);
} }
mUpdateSkillArea = true;
} }
void ReviewDialog::setHealth(const MWMechanics::DynamicStat<float>& value) void ReviewDialog::setHealth(const MWMechanics::DynamicStat<float>& value)
@ -170,7 +185,11 @@ namespace MWGui
if (attr == mAttributeWidgets.end()) if (attr == mAttributeWidgets.end())
return; return;
if (attr->second->getAttributeValue() != value)
{
attr->second->setAttributeValue(value); attr->second->setAttributeValue(value);
mUpdateSkillArea = true;
}
} }
void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value) void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value)
@ -191,6 +210,7 @@ namespace MWGui
widget->_setWidgetState(state); widget->_setWidgetState(state);
} }
mUpdateSkillArea = true;
} }
void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor) void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
@ -211,7 +231,7 @@ namespace MWGui
mMiscSkills.push_back(skill); mMiscSkills.push_back(skill);
} }
updateSkillArea(); mUpdateSkillArea = true;
} }
void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
@ -245,7 +265,7 @@ namespace MWGui
skillNameWidget->setCaption(text); skillNameWidget->setCaption(text);
skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Default);
skillValueWidget->setCaption(value); skillValueWidget->setCaption(value);
skillValueWidget->_setWidgetState(state); skillValueWidget->_setWidgetState(state);
skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
@ -273,6 +293,20 @@ namespace MWGui
coord2.top += sLineHeight; coord2.top += sLineHeight;
} }
void ReviewDialog::addItem(const ESM::Spell* spell, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2)
{
Widgets::MWSpellPtr widget = mSkillView->createWidget<Widgets::MWSpell>("MW_StatName", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
widget->setSpellId(spell->mId);
widget->setUserString("ToolTipType", "Spell");
widget->setUserString("Spell", spell->mId);
widget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
mSkillWidgets.push_back(widget);
coord1.top += sLineHeight;
coord2.top += sLineHeight;
}
void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{ {
// Add a line separator if there are items above // Add a line separator if there are items above
@ -332,6 +366,80 @@ namespace MWGui
if (!mMiscSkills.empty()) if (!mMiscSkills.empty())
addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2);
// starting spells
std::vector<std::string> spells;
const ESM::Race* race = NULL;
if (!mRaceId.empty())
race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(mRaceId);
int skills[ESM::Skill::Length];
for (int i=0; i<ESM::Skill::Length; ++i)
skills[i] = mSkillValues.find(i)->second.getBase();
int attributes[ESM::Attribute::Length];
for (int i=0; i<ESM::Attribute::Length; ++i)
attributes[i] = mAttributeWidgets[i]->getAttributeValue().getBase();
std::vector<std::string> selectedSpells = MWMechanics::autoCalcPlayerSpells(skills, attributes, race);
for (std::vector<std::string>::iterator iter = selectedSpells.begin(); iter != selectedSpells.end(); ++iter)
{
std::string lower = Misc::StringUtils::lowerCase(*iter);
if (std::find(spells.begin(), spells.end(), lower) == spells.end())
spells.push_back(lower);
}
if (race)
{
for (std::vector<std::string>::const_iterator iter = race->mPowers.mList.begin();
iter != race->mPowers.mList.end(); ++iter)
{
std::string lower = Misc::StringUtils::lowerCase(*iter);
if (std::find(spells.begin(), spells.end(), lower) == spells.end())
spells.push_back(lower);
}
}
if (!mBirthSignId.empty())
{
const ESM::BirthSign* sign = MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find(mBirthSignId);
for (std::vector<std::string>::const_iterator iter = sign->mPowers.mList.begin();
iter != sign->mPowers.mList.end(); ++iter)
{
std::string lower = Misc::StringUtils::lowerCase(*iter);
if (std::find(spells.begin(), spells.end(), lower) == spells.end())
spells.push_back(lower);
}
}
if (!mSkillWidgets.empty())
addSeparator(coord1, coord2);
addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sTypeAbility", "Abilities"), coord1, coord2);
for (std::vector<std::string>::const_iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(*iter);
if (spell->mData.mType == ESM::Spell::ST_Ability)
addItem(spell, coord1, coord2);
}
addSeparator(coord1, coord2);
addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sTypePower", "Powers"), coord1, coord2);
for (std::vector<std::string>::const_iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(*iter);
if (spell->mData.mType == ESM::Spell::ST_Power)
addItem(spell, coord1, coord2);
}
addSeparator(coord1, coord2);
addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sTypeSpell", "Spells"), coord1, coord2);
for (std::vector<std::string>::const_iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(*iter);
if (spell->mData.mType == ESM::Spell::ST_Spell)
addItem(spell, coord1, coord2);
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
mSkillView->setVisibleVScroll(false); mSkillView->setVisibleVScroll(false);
mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top));

@ -6,6 +6,11 @@
#include "windowbase.hpp" #include "windowbase.hpp"
#include "widgets.hpp" #include "widgets.hpp"
namespace ESM
{
struct Spell;
}
namespace MWGui namespace MWGui
{ {
class WindowManager; class WindowManager;
@ -42,6 +47,8 @@ namespace MWGui
virtual void open(); virtual void open();
void onFrame(float duration);
// Events // Events
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int; typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;
@ -75,6 +82,7 @@ namespace MWGui
void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addItem(const ESM::Spell* spell, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void updateSkillArea(); void updateSkillArea();
static const int sLineHeight; static const int sLineHeight;
@ -92,6 +100,8 @@ namespace MWGui
std::string mName, mRaceId, mBirthSignId; std::string mName, mRaceId, mBirthSignId;
ESM::Class mKlass; ESM::Class mKlass;
std::vector<MyGUI::Widget*> mSkillWidgets; //< Skills and other information std::vector<MyGUI::Widget*> mSkillWidgets; //< Skills and other information
bool mUpdateSkillArea;
}; };
} }
#endif #endif

@ -466,9 +466,6 @@ namespace MWGui
else else
actions = MWBase::Environment::get().getInputManager()->getActionControllerSorting(); actions = MWBase::Environment::get().getInputManager()->getActionControllerSorting();
const int h = 18;
const int w = mControlsBox->getWidth() - 28;
int curH = 0;
for (std::vector<int>::const_iterator it = actions.begin(); it != actions.end(); ++it) for (std::vector<int>::const_iterator it = actions.begin(); it != actions.end(); ++it)
{ {
std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it);
@ -481,16 +478,15 @@ namespace MWGui
else else
binding = MWBase::Environment::get().getInputManager()->getActionControllerBindingName(*it); binding = MWBase::Environment::get().getInputManager()->getActionControllerBindingName(*it);
Gui::SharedStateButton* leftText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); Gui::SharedStateButton* leftText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(), MyGUI::Align::Default);
leftText->setCaptionWithReplacing(desc); leftText->setCaptionWithReplacing(desc);
Gui::SharedStateButton* rightText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); Gui::SharedStateButton* rightText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(), MyGUI::Align::Default);
rightText->setCaptionWithReplacing(binding); rightText->setCaptionWithReplacing(binding);
rightText->setTextAlign (MyGUI::Align::Right); rightText->setTextAlign (MyGUI::Align::Right);
rightText->setUserData(*it); // save the action id for callbacks rightText->setUserData(*it); // save the action id for callbacks
rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction);
rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel); rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel);
curH += h;
Gui::ButtonGroup group; Gui::ButtonGroup group;
group.push_back(leftText); group.push_back(leftText);
@ -498,9 +494,25 @@ namespace MWGui
Gui::SharedStateButton::createButtonGroup(group); Gui::SharedStateButton::createButtonGroup(group);
} }
layoutControlsBox();
}
void SettingsWindow::layoutControlsBox()
{
const int h = 18;
const int w = mControlsBox->getWidth() - 28;
const int noWidgetsInRow = 2;
const int totalH = mControlsBox->getChildCount() / noWidgetsInRow * h;
for (size_t i = 0; i < mControlsBox->getChildCount(); i++)
{
MyGUI::Widget * widget = mControlsBox->getChildAt(i);
widget->setCoord(0, i / noWidgetsInRow * h, w, h);
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
mControlsBox->setVisibleVScroll(false); mControlsBox->setVisibleVScroll(false);
mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(totalH, mControlsBox->getHeight()));
mControlsBox->setVisibleVScroll(true); mControlsBox->setVisibleVScroll(true);
} }
@ -556,7 +568,7 @@ namespace MWGui
void SettingsWindow::onWindowResize(MyGUI::Window *_sender) void SettingsWindow::onWindowResize(MyGUI::Window *_sender)
{ {
updateControlsBox(); layoutControlsBox();
} }
void SettingsWindow::resetScrollbars() void SettingsWindow::resetScrollbars()

@ -67,6 +67,8 @@ namespace MWGui
void configureWidgets(MyGUI::Widget* widget); void configureWidgets(MyGUI::Widget* widget);
void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value); void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value);
void layoutControlsBox();
private: private:
void resetScrollbars(); void resetScrollbars();
}; };

@ -1,5 +1,7 @@
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include <iostream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadalch.hpp> #include <components/esm/loadalch.hpp>
@ -14,9 +16,14 @@
#include <components/esm/loadprob.hpp> #include <components/esm/loadprob.hpp>
#include <components/esm/loadrepa.hpp> #include <components/esm/loadrepa.hpp>
#include <components/esm/loadweap.hpp> #include <components/esm/loadweap.hpp>
#include <components/esm/loadench.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/nullaction.hpp" #include "../mwworld/nullaction.hpp"
#include "../mwworld/esmstore.hpp"
namespace namespace
{ {
@ -139,6 +146,31 @@ namespace MWGui
return false; return false;
} }
if ((mFilter & Filter_OnlyRepairable) && (
!base.getClass().hasItemHealth(base)
|| (base.getClass().getItemHealth(base) == base.getClass().getItemMaxHealth(base))
|| (base.getTypeName() != typeid(ESM::Weapon).name()
&& base.getTypeName() != typeid(ESM::Armor).name())))
return false;
if (mFilter & Filter_OnlyRechargable)
{
if (!(item.mFlags & ItemStack::Flag_Enchanted))
return false;
std::string enchId = base.getClass().getEnchantment(base);
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchId);
if (!ench)
{
std::cerr << "Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId() << std::endl;
return false;
}
if (base.getCellRef().getEnchantmentCharge() >= ench->mData.mCharge
|| base.getCellRef().getEnchantmentCharge() == -1)
return false;
}
return true; return true;
} }

@ -39,6 +39,8 @@ namespace MWGui
static const int Filter_OnlyEnchantable = (1<<2); static const int Filter_OnlyEnchantable = (1<<2);
static const int Filter_OnlyChargedSoulstones = (1<<3); static const int Filter_OnlyChargedSoulstones = (1<<3);
static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action
static const int Filter_OnlyRepairable = (1<<5);
static const int Filter_OnlyRechargable = (1<<6);
private: private:

@ -11,6 +11,7 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/player.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spells.hpp" #include "../mwmechanics/spells.hpp"
@ -122,8 +123,15 @@ namespace MWGui
const ESM::Spell* spell = const ESM::Spell* spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId); MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
if (spell->mData.mFlags & ESM::Spell::F_Always MWWorld::Ptr player = MWMechanics::getPlayer();
|| spell->mData.mType == ESM::Spell::ST_Power) std::string raceId = player.get<ESM::NPC>()->mBase->mRace;
const std::string& signId =
MWBase::Environment::get().getWorld()->getPlayer().getBirthSign();
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceId);
const ESM::BirthSign* birthsign = MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find(signId);
// can't delete racial spells, birthsign spells or powers
if (race->mPowers.exists(spell->mId) || birthsign->mPowers.exists(spell->mId) || spell->mData.mType == ESM::Spell::ST_Power)
{ {
MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}");
} }

@ -256,7 +256,7 @@ namespace MWGui
std::string key = it->first.substr(0, underscorePos); std::string key = it->first.substr(0, underscorePos);
std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1));
std::string type = "Property"; type = "Property";
size_t caretPos = key.find("^"); size_t caretPos = key.find("^");
if (caretPos != std::string::npos) if (caretPos != std::string::npos)
{ {

@ -109,6 +109,7 @@
#include "container.hpp" #include "container.hpp"
#include "controllers.hpp" #include "controllers.hpp"
#include "jailscreen.hpp" #include "jailscreen.hpp"
#include "itemchargeview.hpp"
namespace MWGui namespace MWGui
{ {
@ -223,6 +224,7 @@ namespace MWGui
MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>("Layer"); MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>("Layer");
BookPage::registerMyGUIComponents (); BookPage::registerMyGUIComponents ();
ItemView::registerComponents(); ItemView::registerComponents();
ItemChargeView::registerComponents();
ItemWidget::registerComponents(); ItemWidget::registerComponents();
SpellView::registerComponents(); SpellView::registerComponents();
Gui::registerAllWidgets(); Gui::registerAllWidgets();
@ -1004,6 +1006,9 @@ namespace MWGui
mScreenFader->update(frameDuration); mScreenFader->update(frameDuration);
mDebugWindow->onFrame(frameDuration); mDebugWindow->onFrame(frameDuration);
if (mCharGen)
mCharGen->onFrame(frameDuration);
} }
void WindowManager::changeCell(const MWWorld::CellStore* cell) void WindowManager::changeCell(const MWWorld::CellStore* cell)

@ -16,6 +16,9 @@
#include <components/sdlutil/sdlinputwrapper.hpp> #include <components/sdlutil/sdlinputwrapper.hpp>
#include <components/sdlutil/sdlvideowrapper.hpp> #include <components/sdlutil/sdlvideowrapper.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/controlsstate.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -1574,6 +1577,44 @@ namespace MWInput
mInputBinder->removeJoystickButtonBinding (mFakeDeviceID, mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE)); mInputBinder->removeJoystickButtonBinding (mFakeDeviceID, mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE));
} }
int InputManager::countSavedGameRecords() const
{
return 1;
}
void InputManager::write(ESM::ESMWriter& writer, Loading::Listener& /*progress*/)
{
ESM::ControlsState controls;
controls.mViewSwitchDisabled = !getControlSwitch("playerviewswitch");
controls.mControlsDisabled = !getControlSwitch("playercontrols");
controls.mJumpingDisabled = !getControlSwitch("playerjumping");
controls.mLookingDisabled = !getControlSwitch("playerlooking");
controls.mVanityModeDisabled = !getControlSwitch("vanitymode");
controls.mWeaponDrawingDisabled = !getControlSwitch("playerfighting");
controls.mSpellDrawingDisabled = !getControlSwitch("playermagic");
writer.startRecord (ESM::REC_INPU);
controls.save(writer);
writer.endRecord (ESM::REC_INPU);
}
void InputManager::readRecord(ESM::ESMReader& reader, uint32_t type)
{
if (type == ESM::REC_INPU)
{
ESM::ControlsState controls;
controls.load(reader);
toggleControlSwitch("playerviewswitch", !controls.mViewSwitchDisabled);
toggleControlSwitch("playercontrols", !controls.mControlsDisabled);
toggleControlSwitch("playerjumping", !controls.mJumpingDisabled);
toggleControlSwitch("playerlooking", !controls.mLookingDisabled);
toggleControlSwitch("vanitymode", !controls.mVanityModeDisabled);
toggleControlSwitch("playerfighting", !controls.mWeaponDrawingDisabled);
toggleControlSwitch("playermagic", !controls.mSpellDrawingDisabled);
}
}
void InputManager::resetToDefaultKeyBindings() void InputManager::resetToDefaultKeyBindings()
{ {
loadKeyDefaults(true); loadKeyDefaults(true);

@ -149,6 +149,10 @@ namespace MWInput
void clearAllKeyBindings (ICS::Control* control); void clearAllKeyBindings (ICS::Control* control);
void clearAllControllerBindings (ICS::Control* control); void clearAllControllerBindings (ICS::Control* control);
virtual int countSavedGameRecords() const;
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress);
virtual void readRecord(ESM::ESMReader& reader, uint32_t type);
private: private:
SDL_Window* mWindow; SDL_Window* mWindow;
bool mWindowVisible; bool mWindowVisible;

@ -512,7 +512,7 @@ namespace MWMechanics
if (magnitude > 0 && remainingTime > 0 && remainingTime < mDuration) if (magnitude > 0 && remainingTime > 0 && remainingTime < mDuration)
{ {
CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);
effectTick(creatureStats, mActor, key, magnitude * remainingTime); if (effectTick(creatureStats, mActor, key, magnitude * remainingTime))
creatureStats.getMagicEffects().add(key, -magnitude); creatureStats.getMagicEffects().add(key, -magnitude);
} }
} }
@ -527,8 +527,10 @@ namespace MWMechanics
if (duration > 0) if (duration > 0)
{ {
// apply correct magnitude for tickable effects that have just expired, // Apply correct magnitude for tickable effects that have just expired,
// in case duration > remaining time of effect // in case duration > remaining time of effect.
// One case where this will happen is when the player uses the rest/wait command
// while there is a tickable effect active that should expire before the end of the rest/wait.
ExpiryVisitor visitor(ptr, duration); ExpiryVisitor visitor(ptr, duration);
creatureStats.getActiveSpells().visitEffectSources(visitor); creatureStats.getActiveSpells().visitEffectSources(visitor);

@ -41,9 +41,7 @@ namespace MWMechanics
if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range
{ {
// activate when reached // activate when reached
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
MWBase::Environment::get().getWorld()->activate(target, actor); MWBase::Environment::get().getWorld()->activate(target, actor);
return true; return true;
} }

@ -18,6 +18,7 @@
#include "character.hpp" #include "character.hpp"
#include "aicombataction.hpp" #include "aicombataction.hpp"
#include "combat.hpp" #include "combat.hpp"
#include "coordinateconverter.hpp"
namespace namespace
{ {
@ -51,22 +52,39 @@ namespace MWMechanics
ESM::Position mShortcutFailPos; ESM::Position mShortcutFailPos;
MWMechanics::Movement mMovement; MWMechanics::Movement mMovement;
enum FleeState
{
FleeState_None,
FleeState_Idle,
FleeState_RunBlindly,
FleeState_RunToDestination
};
FleeState mFleeState;
bool mLOS;
float mUpdateLOSTimer;
float mFleeBlindRunTimer;
ESM::Pathgrid::Point mFleeDest;
AiCombatStorage(): AiCombatStorage():
mAttackCooldown(0), mAttackCooldown(0.0f),
mTimerReact(AI_REACTION_TIME), mTimerReact(AI_REACTION_TIME),
mTimerCombatMove(0), mTimerCombatMove(0.0f),
mReadyToAttack(false), mReadyToAttack(false),
mAttack(false), mAttack(false),
mAttackRange(0), mAttackRange(0.0f),
mCombatMove(false), mCombatMove(false),
mLastTargetPos(0,0,0), mLastTargetPos(0,0,0),
mCell(NULL), mCell(NULL),
mCurrentAction(), mCurrentAction(),
mActionCooldown(0), mActionCooldown(0.0f),
mStrength(), mStrength(),
mForceNoShortcut(false), mForceNoShortcut(false),
mShortcutFailPos(), mShortcutFailPos(),
mMovement() mMovement(),
mFleeState(FleeState_None),
mLOS(false),
mUpdateLOSTimer(0.0f),
mFleeBlindRunTimer(0.0f)
{} {}
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
@ -76,6 +94,10 @@ namespace MWMechanics
const ESM::Weapon* weapon, bool distantCombat); const ESM::Weapon* weapon, bool distantCombat);
void updateAttack(CharacterController& characterController); void updateAttack(CharacterController& characterController);
void stopAttack(); void stopAttack();
void startFleeing();
void stopFleeing();
bool isFleeing();
}; };
AiCombat::AiCombat(const MWWorld::Ptr& actor) : AiCombat::AiCombat(const MWWorld::Ptr& actor) :
@ -157,16 +179,27 @@ namespace MWMechanics
|| target.getClass().getCreatureStats(target).isDead()) || target.getClass().getCreatureStats(target).isDead())
return true; return true;
if (storage.mCurrentAction.get()) // need to wait to init action with it's attack range if (!storage.isFleeing())
{ {
//Update every frame if (storage.mCurrentAction.get()) // need to wait to init action with its attack range
bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, storage.mAttackRange); {
//Update every frame. UpdateLOS uses a timer, so the LOS check does not happen every frame.
updateLOS(actor, target, duration, storage);
float targetReachedTolerance = 0.0f;
if (storage.mLOS)
targetReachedTolerance = storage.mAttackRange;
bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, targetReachedTolerance);
if (is_target_reached) storage.mReadyToAttack = true; if (is_target_reached) storage.mReadyToAttack = true;
} }
storage.updateCombatMove(duration); storage.updateCombatMove(duration);
if (storage.mReadyToAttack) updateActorsMovement(actor, duration, storage); if (storage.mReadyToAttack) updateActorsMovement(actor, duration, storage);
storage.updateAttack(characterController); storage.updateAttack(characterController);
}
else
{
updateFleeing(actor, target, duration, storage);
}
storage.mActionCooldown -= duration; storage.mActionCooldown -= duration;
float& timerReact = storage.mTimerReact; float& timerReact = storage.mTimerReact;
@ -185,12 +218,6 @@ namespace MWMechanics
void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
{ {
if (isTargetMagicallyHidden(target))
{
storage.stopAttack();
return; // TODO: run away instead of doing nothing
}
const MWWorld::CellStore*& currentCell = storage.mCell; const MWWorld::CellStore*& currentCell = storage.mCell;
bool cellChange = currentCell && (actor.getCell() != currentCell); bool cellChange = currentCell && (actor.getCell() != currentCell);
if(!currentCell || cellChange) if(!currentCell || cellChange)
@ -198,29 +225,60 @@ namespace MWMechanics
currentCell = actor.getCell(); currentCell = actor.getCell();
} }
bool forceFlee = false;
if (!canFight(actor, target))
{
storage.stopAttack();
characterController.setAttackingOrSpell(false);
storage.mActionCooldown = 0.f;
forceFlee = true;
}
const MWWorld::Class& actorClass = actor.getClass(); const MWWorld::Class& actorClass = actor.getClass();
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
float& actionCooldown = storage.mActionCooldown; float& actionCooldown = storage.mActionCooldown;
boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
if (!forceFlee)
{
if (actionCooldown > 0) if (actionCooldown > 0)
return; return;
float &rangeAttack = storage.mAttackRange;
boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
if (characterController.readyToPrepareAttack()) if (characterController.readyToPrepareAttack())
{ {
currentAction = prepareNextAction(actor, target); currentAction = prepareNextAction(actor, target);
actionCooldown = currentAction->getActionCooldown(); actionCooldown = currentAction->getActionCooldown();
} }
}
else
{
currentAction.reset(new ActionFlee());
actionCooldown = currentAction->getActionCooldown();
}
const ESM::Weapon *weapon = NULL; if (!currentAction)
bool isRangedCombat = false; return;
if (currentAction.get())
if (storage.isFleeing() != currentAction->isFleeing())
{ {
if (currentAction->isFleeing())
{
storage.startFleeing();
MWBase::Environment::get().getDialogueManager()->say(actor, "flee");
return;
}
else
storage.stopFleeing();
}
bool isRangedCombat = false;
float &rangeAttack = storage.mAttackRange;
rangeAttack = currentAction->getCombatRange(isRangedCombat); rangeAttack = currentAction->getCombatRange(isRangedCombat);
// Get weapon characteristics // Get weapon characteristics
weapon = currentAction->getWeapon(); const ESM::Weapon* weapon = currentAction->getWeapon();
}
ESM::Position pos = actor.getRefData().getPosition(); ESM::Position pos = actor.getRefData().getPosition();
osg::Vec3f vActorPos(pos.asVec3()); osg::Vec3f vActorPos(pos.asVec3());
@ -229,19 +287,7 @@ namespace MWMechanics
osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target);
float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);
if (!currentAction) storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS);
return;
storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack);
// can't fight if attacker can't go where target is. E.g. A fish can't attack person on land.
if (distToTarget > rangeAttack
&& !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
{
// TODO: start fleeing?
storage.stopAttack();
return;
}
if (storage.mReadyToAttack) if (storage.mReadyToAttack)
{ {
@ -267,6 +313,111 @@ namespace MWMechanics
} }
} }
void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)
{
static const float LOS_UPDATE_DURATION = 0.5f;
if (storage.mUpdateLOSTimer <= 0.f)
{
storage.mLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target);
storage.mUpdateLOSTimer = LOS_UPDATE_DURATION;
}
else
storage.mUpdateLOSTimer -= duration;
}
void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)
{
static const float BLIND_RUN_DURATION = 1.0f;
updateLOS(actor, target, duration, storage);
AiCombatStorage::FleeState& state = storage.mFleeState;
switch (state)
{
case AiCombatStorage::FleeState_None:
return;
case AiCombatStorage::FleeState_Idle:
{
float triggerDist = getMaxAttackDistance(target);
if (storage.mLOS &&
(triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist))
{
const ESM::Pathgrid* pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*storage.mCell->getCell());
bool runFallback = true;
if (pathgrid && !actor.getClass().isPureWaterCreature(actor))
{
ESM::Pathgrid::PointList points;
CoordinateConverter coords(storage.mCell->getCell());
osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();
coords.toLocal(localPos);
int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, localPos);
for (int i = 0; i < static_cast<int>(pathgrid->mPoints.size()); i++)
{
if (i != closestPointIndex && storage.mCell->isPointConnected(closestPointIndex, i))
{
points.push_back(pathgrid->mPoints[static_cast<size_t>(i)]);
}
}
if (!points.empty())
{
ESM::Pathgrid::Point dest = points[Misc::Rng::rollDice(points.size())];
coords.toWorld(dest);
state = AiCombatStorage::FleeState_RunToDestination;
storage.mFleeDest = ESM::Pathgrid::Point(dest.mX, dest.mY, dest.mZ);
runFallback = false;
}
}
if (runFallback)
{
state = AiCombatStorage::FleeState_RunBlindly;
storage.mFleeBlindRunTimer = 0.0f;
}
}
}
break;
case AiCombatStorage::FleeState_RunBlindly:
{
// timer to prevent twitchy movement that can be observed in vanilla MW
if (storage.mFleeBlindRunTimer < BLIND_RUN_DURATION)
{
storage.mFleeBlindRunTimer += duration;
storage.mMovement.mRotation[2] = osg::PI + getZAngleToDir(target.getRefData().getPosition().asVec3()-actor.getRefData().getPosition().asVec3());
storage.mMovement.mPosition[1] = 1;
updateActorsMovement(actor, duration, storage);
}
else
state = AiCombatStorage::FleeState_Idle;
}
break;
case AiCombatStorage::FleeState_RunToDestination:
{
static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fFleeDistance")->getFloat();
float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length();
if ((dist > fFleeDistance && !storage.mLOS)
|| pathTo(actor, storage.mFleeDest, duration))
{
state = AiCombatStorage::FleeState_Idle;
}
}
break;
};
}
void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage) void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage)
{ {
// apply combat movement // apply combat movement
@ -446,6 +597,26 @@ namespace MWMechanics
mReadyToAttack = false; mReadyToAttack = false;
mAttack = false; mAttack = false;
} }
void AiCombatStorage::startFleeing()
{
stopFleeing();
mFleeState = FleeState_Idle;
}
void AiCombatStorage::stopFleeing()
{
mMovement.mPosition[0] = 0;
mMovement.mPosition[1] = 0;
mMovement.mPosition[2] = 0;
mFleeState = FleeState_None;
mFleeDest = ESM::Pathgrid::Point(0, 0, 0);
}
bool AiCombatStorage::isFleeing()
{
return mFleeState != FleeState_None;
}
} }

@ -61,6 +61,10 @@ namespace MWMechanics
void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);
void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);
void updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);
/// Transfer desired movement (from AiCombatStorage) to Actor /// Transfer desired movement (from AiCombatStorage) to Actor
void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage); void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage);
void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis,

@ -5,14 +5,17 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp" #include "../mwworld/actionequip.hpp"
#include "../mwworld/cellstore.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "spellcasting.hpp" #include "spellcasting.hpp"
#include "combat.hpp"
namespace namespace
{ {
@ -517,6 +520,7 @@ namespace MWMechanics
Spells& spells = actor.getClass().getCreatureStats(actor).getSpells(); Spells& spells = actor.getClass().getCreatureStats(actor).getSpells();
float bestActionRating = 0.f; float bestActionRating = 0.f;
float antiFleeRating = 0.f;
// Default to hand-to-hand combat // Default to hand-to-hand combat
boost::shared_ptr<Action> bestAction (new ActionWeapon(MWWorld::Ptr())); boost::shared_ptr<Action> bestAction (new ActionWeapon(MWWorld::Ptr()));
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
@ -536,6 +540,7 @@ namespace MWMechanics
{ {
bestActionRating = rating; bestActionRating = rating;
bestAction.reset(new ActionPotion(*it)); bestAction.reset(new ActionPotion(*it));
antiFleeRating = std::numeric_limits<float>::max();
} }
} }
@ -546,6 +551,7 @@ namespace MWMechanics
{ {
bestActionRating = rating; bestActionRating = rating;
bestAction.reset(new ActionEnchantedItem(it)); bestAction.reset(new ActionEnchantedItem(it));
antiFleeRating = std::numeric_limits<float>::max();
} }
} }
@ -593,6 +599,7 @@ namespace MWMechanics
bestActionRating = rating; bestActionRating = rating;
bestAction.reset(new ActionWeapon(*it, ammo)); bestAction.reset(new ActionWeapon(*it, ammo));
antiFleeRating = vanillaRateWeaponAndAmmo(*it, ammo, actor, enemy);
} }
} }
} }
@ -606,13 +613,308 @@ namespace MWMechanics
{ {
bestActionRating = rating; bestActionRating = rating;
bestAction.reset(new ActionSpell(spell->mId)); bestAction.reset(new ActionSpell(spell->mId));
antiFleeRating = vanillaRateSpell(spell, actor, enemy);
} }
} }
if (makeFleeDecision(actor, enemy, antiFleeRating))
bestAction.reset(new ActionFlee());
if (bestAction.get()) if (bestAction.get())
bestAction->prepare(actor); bestAction->prepare(actor);
return bestAction; return bestAction;
} }
float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool minusZDist)
{
osg::Vec3f actor1Pos = actor1.getRefData().getPosition().asVec3();
osg::Vec3f actor2Pos = actor2.getRefData().getPosition().asVec3();
float dist = (actor1Pos - actor2Pos).length();
if (minusZDist)
dist -= std::abs(actor1Pos.z() - actor2Pos.z());
return (dist
- MWBase::Environment::get().getWorld()->getHalfExtents(actor1).y()
- MWBase::Environment::get().getWorld()->getHalfExtents(actor2).y());
}
float getMaxAttackDistance(const MWWorld::Ptr& actor)
{
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
std::string selectedSpellId = stats.getSpells().getSelectedSpell();
MWWorld::Ptr selectedEnchItem;
MWWorld::Ptr activeWeapon, activeAmmo;
if (actor.getClass().hasInventoryStore(actor))
{
MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);
MWWorld::ContainerStoreIterator item = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon)
activeWeapon = *item;
item = invStore.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon)
activeAmmo = *item;
if (invStore.getSelectedEnchantItem() != invStore.end())
selectedEnchItem = *invStore.getSelectedEnchantItem();
}
float dist = 1.0f;
if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty())
{
static const float fHandToHandReach = gmst.find("fHandToHandReach")->getFloat();
dist = fHandToHandReach;
}
else if (stats.getDrawState() == MWMechanics::DrawState_Spell)
{
dist = 1.0f;
if (!selectedSpellId.empty())
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(selectedSpellId);
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
{
if (effectIt->mArea == ESM::RT_Target)
{
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
dist = effect->mData.mSpeed;
break;
}
}
}
else if (!selectedEnchItem.isEmpty())
{
std::string enchId = selectedEnchItem.getClass().getEnchantment(selectedEnchItem);
if (!enchId.empty())
{
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchId);
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
ench->mEffects.mList.begin(); effectIt != ench->mEffects.mList.end(); ++effectIt)
{
if (effectIt->mArea == ESM::RT_Target)
{
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
dist = effect->mData.mSpeed;
break;
}
}
}
}
static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat();
dist *= std::max(1000.0f, fTargetSpellMaxSpeed);
}
else if (!activeWeapon.isEmpty())
{
const ESM::Weapon* esmWeap = activeWeapon.get<ESM::Weapon>()->mBase;
if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
{
static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
dist = fTargetSpellMaxSpeed;
if (!activeAmmo.isEmpty())
{
const ESM::Weapon* esmAmmo = activeAmmo.get<ESM::Weapon>()->mBase;
dist *= esmAmmo->mData.mSpeed;
}
}
else if (esmWeap->mData.mReach > 1)
{
dist = esmWeap->mData.mReach;
}
}
dist = (dist > 0.f) ? dist : 1.0f;
static const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->getFloat();
float combatDistance = fCombatDistance;
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
combatDistance *= (fCombatDistanceWerewolfMod + 1.0f);
if (dist < combatDistance)
dist *= combatDistance;
return dist;
}
bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
{
ESM::Position actorPos = actor.getRefData().getPosition();
ESM::Position enemyPos = enemy.getRefData().getPosition();
const CreatureStats& enemyStats = enemy.getClass().getCreatureStats(enemy);
if (enemyStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0
|| enemyStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 0)
{
if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(enemy, actor))
return false;
}
if (actor.getClass().isPureWaterCreature(actor))
{
if (!MWBase::Environment::get().getWorld()->isWading(enemy))
return false;
}
float atDist = getMaxAttackDistance(actor);
if (atDist > getDistanceMinusHalfExtents(actor, enemy)
&& atDist > std::abs(actorPos.pos[2] - enemyPos.pos[2]))
{
if (MWBase::Environment::get().getWorld()->getLOS(actor, enemy))
return true;
}
if (actor.getClass().isPureFlyingCreature(actor) || actor.getClass().isPureLandCreature(actor))
{
if (MWBase::Environment::get().getWorld()->isSwimming(enemy))
return false;
}
if (actor.getClass().isBipedal(actor) || !actor.getClass().canFly(actor))
{
if (enemy.getClass().getCreatureStats(enemy).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0)
{
float attackDistance = getMaxAttackDistance(actor);
if ((attackDistance + actorPos.pos[2]) < enemyPos.pos[2])
{
if (enemy.getCell()->isExterior())
{
if (attackDistance < (enemyPos.pos[2] - MWBase::Environment::get().getWorld()->getTerrainHeightAt(enemyPos.asVec3())))
return false;
}
}
}
}
if (!actor.getClass().canWalk(actor) && !actor.getClass().isBipedal(actor))
return true;
if (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0)
return true;
if (MWBase::Environment::get().getWorld()->isSwimming(actor))
return true;
if (getDistanceMinusHalfExtents(actor, enemy, true) <= 0.0f)
return false;
return true;
}
float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
{
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->getFloat();
static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->getFloat();
float mult = fAIMagicSpellMult;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
{
if (effectIt->mArea == ESM::RT_Target)
{
if (!MWBase::Environment::get().getWorld()->isSwimming(enemy))
mult = fAIRangeMagicSpellMult;
else
mult = 0.0f;
break;
}
}
return MWMechanics::getSpellSuccessChance(spell, actor) * mult;
}
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
{
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat();
static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat();
static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat();
if (weapon.isEmpty())
return 0.f;
float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f;
float chopMult = fAIMeleeWeaponMult;
float bonusDamage = 0.f;
const ESM::Weapon* esmWeap = weapon.get<ESM::Weapon>()->mBase;
if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
{
if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy))
{
bonusDamage = ammo.get<ESM::Weapon>()->mBase->mData.mChop[1];
chopMult = fAIRangeMeleeWeaponMult;
}
else
chopMult = 0.f;
}
float chopRating = (esmWeap->mData.mChop[1] + bonusDamage) * skillMult * chopMult;
float slashRating = esmWeap->mData.mSlash[1] * skillMult * fAIMeleeWeaponMult;
float thrustRating = esmWeap->mData.mThrust[1] * skillMult * fAIMeleeWeaponMult;
return actor.getClass().getArmorRating(actor) * fAIMeleeArmorMult
+ std::max(std::max(chopRating, slashRating), thrustRating);
}
float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
{
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int flee = stats.getAiSetting(CreatureStats::AI_Flee).getModified();
if (flee >= 100)
return flee;
static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->getFloat();
static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->getFloat();
float healthPercentage = (stats.getHealth().getModified() == 0.0f)
? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified();
float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult;
static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt();
if (enemy.getClass().isNpc() && enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack)
{
static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt();
rating = iWereWolfFleeMod;
}
if (rating != 0.0f)
rating += getFightDistanceBias(actor, enemy);
return rating;
}
bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating)
{
float fleeRating = vanillaRateFlee(actor, enemy);
if (fleeRating < 100.0f)
fleeRating = 0.0f;
if (fleeRating > antiFleeRating)
return true;
// Run away after summoning a creature if we have nothing to use but fists.
if (antiFleeRating == 0.0f && !actor.getClass().getCreatureStats(actor).getSummonedCreatureMap().empty())
return true;
return false;
}
} }

@ -20,6 +20,18 @@ namespace MWMechanics
virtual float getActionCooldown() { return 0.f; } virtual float getActionCooldown() { return 0.f; }
virtual const ESM::Weapon* getWeapon() const { return NULL; }; virtual const ESM::Weapon* getWeapon() const { return NULL; };
virtual bool isAttackingOrSpell() const { return true; } virtual bool isAttackingOrSpell() const { return true; }
virtual bool isFleeing() const { return false; }
};
class ActionFlee : public Action
{
public:
ActionFlee() {}
virtual void prepare(const MWWorld::Ptr& actor) {}
virtual float getCombatRange (bool& isRanged) const { return 0.0f; }
virtual float getActionCooldown() { return 3.0f; }
virtual bool isAttackingOrSpell() const { return false; }
virtual bool isFleeing() const { return true; }
}; };
class ActionSpell : public Action class ActionSpell : public Action
@ -89,6 +101,15 @@ namespace MWMechanics
float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
boost::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); boost::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool minusZDist=false);
float getMaxAttackDistance(const MWWorld::Ptr& actor);
bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating);
} }
#endif #endif

@ -152,7 +152,7 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
if (dist > 450) if (dist > 450)
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
else if (dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold else if (dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshold
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
} }

@ -141,6 +141,79 @@ namespace MWMechanics
return selectedSpells; return selectedSpells;
} }
std::vector<std::string> autoCalcPlayerSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race)
{
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat();
float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];
bool reachedLimit = false;
const ESM::Spell* weakestSpell = NULL;
int minCost = INT_MAX;
std::vector<std::string> selectedSpells;
const MWWorld::Store<ESM::Spell> &spells =
esmStore.get<ESM::Spell>();
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = &*iter;
if (spell->mData.mType != ESM::Spell::ST_Spell)
continue;
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
continue;
if (reachedLimit && spell->mData.mCost <= minCost)
continue;
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
continue;
if (baseMagicka < spell->mData.mCost)
continue;
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat();
if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance)
continue;
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
continue;
selectedSpells.push_back(spell->mId);
if (reachedLimit)
{
std::vector<std::string>::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId);
if (it != selectedSpells.end())
selectedSpells.erase(it);
minCost = INT_MAX;
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
{
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt);
if (testSpell->mData.mCost < minCost)
{
minCost = testSpell->mData.mCost;
weakestSpell = testSpell;
}
}
}
else
{
if (spell->mData.mCost < minCost)
{
weakestSpell = spell;
minCost = weakestSpell->mData.mCost;
}
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt();
if (selectedSpells.size() == iAutoPCSpellMax)
reachedLimit = true;
}
}
return selectedSpells;
}
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes) bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
{ {
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList; const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;

@ -16,6 +16,8 @@ namespace MWMechanics
std::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); std::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
std::vector<std::string> autoCalcPlayerSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
// Helpers // Helpers
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes); bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes);

@ -1049,6 +1049,9 @@ bool CharacterController::updateCreatureState()
mUpperBodyState = UpperCharState_StartToMinAttack; mUpperBodyState = UpperCharState_StartToMinAttack;
mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability()); mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability());
if (weapType == WeapType_HandToHand)
playSwishSound(0.0f);
} }
} }
@ -1370,13 +1373,7 @@ bool CharacterController::updateWeaponState()
} }
else else
{ {
std::string sound = "SwishM"; playSwishSound(attackStrength);
if(attackStrength < 0.5f)
sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack
else if(attackStrength < 1.0f)
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack
else
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack
} }
} }
mAttackStrength = attackStrength; mAttackStrength = attackStrength;
@ -2271,6 +2268,19 @@ void CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target)
mHeadTrackTarget = target; mHeadTrackTarget = target;
} }
void CharacterController::playSwishSound(float attackStrength)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
std::string sound = "Weapon Swish";
if(attackStrength < 0.5f)
sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack
else if(attackStrength < 1.0f)
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack
else
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack
}
void CharacterController::updateHeadTracking(float duration) void CharacterController::updateHeadTracking(float duration)
{ {
const osg::Node* head = mAnimation->getNode("Bip01 Head"); const osg::Node* head = mAnimation->getNode("Bip01 Head");

@ -281,6 +281,8 @@ public:
/// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr. /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr.
void setHeadTrackTarget(const MWWorld::ConstPtr& target); void setHeadTrackTarget(const MWWorld::ConstPtr& target);
void playSwishSound(float attackStrength);
}; };
MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype);

@ -431,4 +431,19 @@ namespace MWMechanics
return true; return true;
} }
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2)
{
osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3());
osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3());
float d = (pos1 - pos2).length();
static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"iFightDistanceBase")->getInt();
static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDistanceMultiplier")->getFloat();
return (iFightDistanceBase - fFightDistanceMultiplier * d);
}
} }

@ -42,6 +42,7 @@ void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon,
/// e.g. If attacker is a fish, is victim in water? Or, if attacker can't swim, is victim on land? /// e.g. If attacker is a fish, is victim in water? Or, if attacker can't swim, is victim on land?
bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2);
} }
#endif #endif

@ -25,25 +25,11 @@
#include "autocalcspell.hpp" #include "autocalcspell.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
#include "combat.hpp"
namespace namespace
{ {
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2)
{
osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3());
osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3());
float d = (pos1 - pos2).length();
static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"iFightDistanceBase")->getInt();
static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDistanceMultiplier")->getFloat();
return (iFightDistanceBase - fFightDistanceMultiplier * d);
}
float getFightDispositionBias(float disposition) float getFightDispositionBias(float disposition)
{ {
static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
@ -212,15 +198,6 @@ namespace MWMechanics
} }
// F_PCStart spells // F_PCStart spells
static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat();
float baseMagicka = fPCbaseMagickaMult * creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase();
bool reachedLimit = false;
const ESM::Spell* weakestSpell = NULL;
int minCost = INT_MAX;
std::vector<std::string> selectedSpells;
const ESM::Race* race = NULL; const ESM::Race* race = NULL;
if (mRaceSelected) if (mRaceSelected)
race = esmStore.get<ESM::Race>().find(player->mRace); race = esmStore.get<ESM::Race>().find(player->mRace);
@ -233,61 +210,7 @@ namespace MWMechanics
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase(); attributes[i] = npcStats.getAttribute(i).getBase();
const MWWorld::Store<ESM::Spell> &spells = std::vector<std::string> selectedSpells = autoCalcPlayerSpells(skills, attributes, race);
esmStore.get<ESM::Spell>();
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = &*iter;
if (spell->mData.mType != ESM::Spell::ST_Spell)
continue;
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
continue;
if (reachedLimit && spell->mData.mCost <= minCost)
continue;
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
continue;
if (baseMagicka < spell->mData.mCost)
continue;
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat();
if (calcAutoCastChance(spell, skills, attributes, -1) < fAutoPCSpellChance)
continue;
if (!attrSkillCheck(spell, skills, attributes))
continue;
selectedSpells.push_back(spell->mId);
if (reachedLimit)
{
std::vector<std::string>::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId);
if (it != selectedSpells.end())
selectedSpells.erase(it);
minCost = INT_MAX;
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
{
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt);
if (testSpell->mData.mCost < minCost)
{
minCost = testSpell->mData.mCost;
weakestSpell = testSpell;
}
}
}
else
{
if (spell->mData.mCost < minCost)
{
weakestSpell = spell;
minCost = weakestSpell->mData.mCost;
}
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt();
if (selectedSpells.size() == iAutoPCSpellMax)
reachedLimit = true;
}
}
for (std::vector<std::string>::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) for (std::vector<std::string>::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it)
creatureStats.getSpells().add(*it); creatureStats.getSpells().add(*it);
@ -313,7 +236,7 @@ namespace MWMechanics
: mWatchedTimeToStartDrowning(0), mWatchedStatsEmpty (true), mUpdatePlayer (true), mClassSelected (false), : mWatchedTimeToStartDrowning(0), mWatchedStatsEmpty (true), mUpdatePlayer (true), mClassSelected (false),
mRaceSelected (false), mAI(true) mRaceSelected (false), mAI(true)
{ {
//buildPlayer no longer here, needs to be done explicitely after all subsystems are up and running //buildPlayer no longer here, needs to be done explicitly after all subsystems are up and running
} }
void MechanicsManager::add(const MWWorld::Ptr& ptr) void MechanicsManager::add(const MWWorld::Ptr& ptr)
@ -1608,27 +1531,23 @@ namespace MWMechanics
player->restoreSkillsAttributes(); player->restoreSkillsAttributes();
} }
// Equipped items other than WerewolfRobe may reference bones that do not even
// exist with the werewolf object root, so make sure to unequip all items
// *before* we become a werewolf.
MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);
invStore.unequipAll(actor);
// Werewolfs can not cast spells, so we need to unset the prepared spell if there is one. // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one.
if (npcStats.getDrawState() == MWMechanics::DrawState_Spell) if (npcStats.getDrawState() == MWMechanics::DrawState_Spell)
npcStats.setDrawState(MWMechanics::DrawState_Nothing); npcStats.setDrawState(MWMechanics::DrawState_Nothing);
npcStats.setWerewolf(werewolf); npcStats.setWerewolf(werewolf);
if(werewolf)
{
MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor); MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor);
if(werewolf)
{
inv.unequipAll(actor);
inv.equip(MWWorld::InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); inv.equip(MWWorld::InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor);
} }
else else
{ {
actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); inv.unequipSlot(MWWorld::InventoryStore::Slot_Robe, actor);
inv.ContainerStore::remove("werewolfrobe", 1, actor);
} }
if(actor == player->getPlayer()) if(actor == player->getPlayer())

@ -19,7 +19,7 @@ namespace MWMechanics
bool proximityToDoor(const MWWorld::Ptr& actor, bool proximityToDoor(const MWWorld::Ptr& actor,
float minSqr = MIN_DIST_TO_DOOR_SQUARED); float minSqr = MIN_DIST_TO_DOOR_SQUARED);
/// Returns door pointer within range. No guarentee is given as too which one /// Returns door pointer within range. No guarantee is given as to which one
/** \return Pointer to the door, or NULL if none exists **/ /** \return Pointer to the door, or NULL if none exists **/
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor,
float minSqr = MIN_DIST_TO_DOOR_SQUARED); float minSqr = MIN_DIST_TO_DOOR_SQUARED);

@ -223,7 +223,7 @@ namespace MWMechanics
return 1 - resistance / 100.f; return 1 - resistance / 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 effect can be applied to the target. If \a castByPlayer, emits a message box on failure.
bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer)
{ {
switch (effectId) switch (effectId)
@ -491,7 +491,7 @@ namespace MWMechanics
appliedLastingEffects.push_back(effect); appliedLastingEffects.push_back(effect);
// For absorb effects, also apply the effect to the caster - but with a negative // For absorb effects, also apply the effect to the caster - but with a negative
// magnitude, since we're transfering stats from the target to the caster // magnitude, since we're transferring stats from the target to the caster
if (!caster.isEmpty() && caster.getClass().isActor()) if (!caster.isEmpty() && caster.getClass().isActor())
{ {
for (int i=0; i<5; ++i) for (int i=0; i<5; ++i)
@ -913,7 +913,7 @@ namespace MWMechanics
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
if (mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx if (animation && mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx even if they are disabled (animation == NULL)
{ {
const ESM::Static* castStatic; const ESM::Static* castStatic;
@ -927,7 +927,7 @@ namespace MWMechanics
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture); animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture);
} }
if (!mCaster.getClass().isActor()) if (animation && !mCaster.getClass().isActor())
animation->addSpellCastGlow(effect); animation->addSpellCastGlow(effect);
static const std::string schools[] = { static const std::string schools[] = {
@ -980,8 +980,10 @@ namespace MWMechanics
if (charge == 0) if (charge == 0)
return false; return false;
// FIXME: charge should be a float, not int so that damage < 1 per frame can be applied. // Store remainder of disintegrate amount (automatically subtracted if > 1)
// This was also a bug in the original engine. item->getCellRef().applyChargeRemainderToBeSubtracted(disintegrate - std::floor(disintegrate));
charge = item->getClass().getItemHealth(*item);
charge -= charge -=
std::min(static_cast<int>(disintegrate), std::min(static_cast<int>(disintegrate),
charge); charge);
@ -1009,10 +1011,10 @@ namespace MWMechanics
creatureStats.setDynamic(index, stat); creatureStats.setDynamic(index, stat);
} }
void effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude) bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude)
{ {
if (magnitude == 0.f) if (magnitude == 0.f)
return; return false;
bool receivedMagicDamage = false; bool receivedMagicDamage = false;
@ -1144,10 +1146,13 @@ namespace MWMechanics
case ESM::MagicEffect::RemoveCurse: case ESM::MagicEffect::RemoveCurse:
actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); actor.getClass().getCreatureStats(actor).getSpells().purgeCurses();
break; break;
default:
return false;
} }
if (receivedMagicDamage && actor == getPlayer()) if (receivedMagicDamage && actor == getPlayer())
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
return true;
} }
} }

@ -59,9 +59,13 @@ namespace MWMechanics
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);
bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer);
int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor);
void effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed
/// @return Was the effect a tickable effect with a magnitude?
bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude);
class CastSpell class CastSpell
{ {

@ -69,7 +69,14 @@ namespace MWPhysics
return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f))); return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f)));
} }
static bool stepMove(const btCollisionObject *colobj, osg::Vec3f &position, enum StepMoveResult
{
Result_Blocked, // unable to move over obstacle
Result_MaxSlope, // unable to end movement on this slope
Result_Success
};
static StepMoveResult stepMove(const btCollisionObject *colobj, osg::Vec3f &position,
const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld) const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld)
{ {
/* /*
@ -120,7 +127,7 @@ namespace MWPhysics
stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld); stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld);
if(stepper.mFraction < std::numeric_limits<float>::epsilon()) if(stepper.mFraction < std::numeric_limits<float>::epsilon())
return false; // didn't even move the smallest representable amount return Result_Blocked; // didn't even move the smallest representable amount
// (TODO: shouldn't this be larger? Why bother with such a small amount?) // (TODO: shouldn't this be larger? Why bother with such a small amount?)
/* /*
@ -138,7 +145,7 @@ namespace MWPhysics
*/ */
tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld); tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld);
if(tracer.mFraction < std::numeric_limits<float>::epsilon()) if(tracer.mFraction < std::numeric_limits<float>::epsilon())
return false; // didn't even move the smallest representable amount return Result_Blocked; // didn't even move the smallest representable amount
/* /*
* Try moving back down sStepSizeDown using stepper. * Try moving back down sStepSizeDown using stepper.
@ -156,22 +163,22 @@ namespace MWPhysics
* ============================================== * ==============================================
*/ */
stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld);
if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) if (getSlope(stepper.mPlaneNormal) > sMaxSlope)
return Result_MaxSlope;
if(stepper.mFraction < 1.0f)
{ {
// don't allow stepping up other actors // don't allow stepping up other actors
if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor)
return false; return Result_Blocked;
// only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
// TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing
// NOTE: caller's variables 'position' & 'remainingTime' are modified here // NOTE: caller's variables 'position' & 'remainingTime' are modified here
position = stepper.mEndPos; position = stepper.mEndPos;
remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance
return true; return Result_Success;
} }
// moved between 0 and just under sStepSize distance but slope was too great, return Result_Blocked;
// or moved full sStepSize distance (FIXME: is this a bug?)
return false;
} }
@ -361,14 +368,15 @@ namespace MWPhysics
osg::Vec3f oldPosition = newPosition; osg::Vec3f oldPosition = newPosition;
// We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over)
// NOTE: stepMove modifies newPosition if successful // NOTE: stepMove modifies newPosition if successful
bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); const float minStep = 10.f;
if (!result) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent StepMoveResult result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld);
if (result == Result_MaxSlope && (velocity*remainingTime).length() < minStep) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent
{ {
osg::Vec3f normalizedVelocity = velocity; osg::Vec3f normalizedVelocity = velocity;
normalizedVelocity.normalize(); normalizedVelocity.normalize();
result = stepMove(colobj, newPosition, normalizedVelocity*10.f, remainingTime, collisionWorld); result = stepMove(colobj, newPosition, normalizedVelocity*minStep, remainingTime, collisionWorld);
} }
if(result) if(result == Result_Success)
{ {
// don't let pure water creatures move out of water after stepMove // don't let pure water creatures move out of water after stepMove
if (ptr.getClass().isPureWaterCreature(ptr) if (ptr.getClass().isPureWaterCreature(ptr)
@ -450,8 +458,8 @@ namespace MWPhysics
if (inertia.z() < 0) if (inertia.z() < 0)
inertia.z() *= slowFall; inertia.z() *= slowFall;
if (slowFall < 1.f) { if (slowFall < 1.f) {
inertia.x() = 0; inertia.x() *= slowFall;
inertia.y() = 0; inertia.y() *= slowFall;
} }
physicActor->setInertialForce(inertia); physicActor->setInertialForce(inertia);
} }
@ -985,6 +993,18 @@ namespace MWPhysics
} }
} }
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
{
const Actor* physicActor = getActor(actor);
const float halfZ = physicActor->getHalfExtents().z();
const osg::Vec3f actorPosition = physicActor->getPosition();
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
ActorTracer tracer;
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld);
return (tracer.mFraction >= 1.0f);
}
osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const
{ {
const Actor* physactor = getActor(actor); const Actor* physactor = getActor(actor);
@ -1309,25 +1329,29 @@ namespace MWPhysics
PtrVelocityList::iterator iter = mMovementQueue.begin(); PtrVelocityList::iterator iter = mMovementQueue.begin();
for(;iter != mMovementQueue.end();++iter) for(;iter != mMovementQueue.end();++iter)
{ {
ActorMap::iterator foundActor = mActors.find(iter->first);
if (foundActor == mActors.end()) // actor was already removed from the scene
continue;
Actor* physicActor = foundActor->second;
float waterlevel = -std::numeric_limits<float>::max(); float waterlevel = -std::numeric_limits<float>::max();
const MWWorld::CellStore *cell = iter->first.getCell(); const MWWorld::CellStore *cell = iter->first.getCell();
if(cell->getCell()->hasWater()) if(cell->getCell()->hasWater())
waterlevel = cell->getWaterLevel(); waterlevel = cell->getWaterLevel();
const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects();
bool waterCollision = false; bool waterCollision = false;
if (effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
&& cell->getCell()->hasWater() {
&& !world->isUnderwater(iter->first.getCell(), if (!world->isUnderwater(iter->first.getCell(), osg::Vec3f(iter->first.getRefData().getPosition().asVec3())))
osg::Vec3f(iter->first.getRefData().getPosition().asVec3())))
waterCollision = true; waterCollision = true;
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(iter->first, waterlevel))
ActorMap::iterator foundActor = mActors.find(iter->first); {
if (foundActor == mActors.end()) // actor was already removed from the scene const osg::Vec3f actorPosition = physicActor->getPosition();
continue; physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel));
Actor* physicActor = foundActor->second; }
}
physicActor->setCanWaterWalk(waterCollision); physicActor->setCanWaterWalk(waterCollision);
// Slow fall reduces fall speed by a factor of (effect magnitude / 200) // Slow fall reduces fall speed by a factor of (effect magnitude / 200)

@ -123,6 +123,8 @@ namespace MWPhysics
bool isOnGround (const MWWorld::Ptr& actor); bool isOnGround (const MWWorld::Ptr& actor);
bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel);
/// Get physical half extents (scaled) of the given actor. /// Get physical half extents (scaled) of the given actor.
osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const; osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const;

@ -686,9 +686,9 @@ namespace MWRender
state.mAutoDisable = autodisable; state.mAutoDisable = autodisable;
mStates[groupname] = state; mStates[groupname] = state;
NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime()));
if (state.mPlaying) if (state.mPlaying)
{ {
NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime()));
while(textkey != textkeys.end() && textkey->first <= state.getTime()) while(textkey != textkeys.end() && textkey->first <= state.getTime())
{ {
handleTextKey(state, groupname, textkey, textkeys); handleTextKey(state, groupname, textkey, textkeys);
@ -976,14 +976,14 @@ namespace MWRender
while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend()) while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend())
{ {
const NifOsg::TextKeyMap &keys = (*animiter)->getTextKeys(); const NifOsg::TextKeyMap &keys2 = (*animiter)->getTextKeys();
const AnimSource::ControllerMap& ctrls2 = (*animiter)->mControllerMap[0]; const AnimSource::ControllerMap& ctrls2 = (*animiter)->mControllerMap[0];
for (AnimSource::ControllerMap::const_iterator it = ctrls2.begin(); it != ctrls2.end(); ++it) for (AnimSource::ControllerMap::const_iterator it = ctrls2.begin(); it != ctrls2.end(); ++it)
{ {
if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName())) if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName()))
{ {
velocity = calcAnimVelocity(keys, it->second, mAccumulate, groupname); velocity = calcAnimVelocity(keys2, it->second, mAccumulate, groupname);
break; break;
} }
} }

@ -302,7 +302,7 @@ protected:
/** Sets the root model of the object. /** Sets the root model of the object.
* *
* Note that you must make sure all animation sources are cleared before reseting the object * Note that you must make sure all animation sources are cleared before resetting the object
* root. All nodes previously retrieved with getNode will also become invalidated. * root. All nodes previously retrieved with getNode will also become invalidated.
* @param forceskeleton Wrap the object root in a Skeleton, even if it contains no skinned parts. Use this if you intend to add skinned parts manually. * @param forceskeleton Wrap the object root in a Skeleton, even if it contains no skinned parts. Use this if you intend to add skinned parts manually.
* @param baseonly If true, then any meshes or particle systems in the model are ignored * @param baseonly If true, then any meshes or particle systems in the model are ignored

@ -221,10 +221,10 @@ namespace MWRender
groupname = "inventoryhandtohand"; groupname = "inventoryhandtohand";
else else
{ {
const std::string &type = iter->getTypeName(); const std::string &typeName = iter->getTypeName();
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) if(typeName == typeid(ESM::Lockpick).name() || typeName == typeid(ESM::Probe).name())
groupname = "inventoryweapononehand"; groupname = "inventoryweapononehand";
else if(type == typeid(ESM::Weapon).name()) else if(typeName == typeid(ESM::Weapon).name())
{ {
MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>(); MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>();

@ -403,6 +403,9 @@ void NpcAnimation::rebuild()
{ {
updateNpcBase(); updateNpcBase();
if (mAlpha != 1.f)
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
} }
@ -503,10 +506,10 @@ void NpcAnimation::updateNpcBase()
if(!isWerewolf) if(!isWerewolf)
{ {
if(Misc::StringUtils::lowerCase(mNpc->mRace).find("argonian") != std::string::npos)
addAnimSource("meshes\\xargonian_swimkna.nif");
if(mNpc->mModel.length() > 0) if(mNpc->mModel.length() > 0)
addAnimSource(Misc::ResourceHelpers::correctActorModelPath("meshes\\" + mNpc->mModel, mResourceSystem->getVFS())); addAnimSource(Misc::ResourceHelpers::correctActorModelPath("meshes\\" + mNpc->mModel, mResourceSystem->getVFS()));
if(Misc::StringUtils::lowerCase(mNpc->mRace).find("argonian") != std::string::npos)
addAnimSource("meshes\\xargonian_swimkna.nif");
} }
} }
else else

@ -458,9 +458,9 @@ namespace MWRender
{ {
mEffectManager->update(dt); mEffectManager->update(dt);
mSky->update(dt); mSky->update(dt);
mWater->update(dt);
} }
mWater->update(dt);
mCamera->update(dt, paused); mCamera->update(dt, paused);
osg::Vec3f focal, cameraPos; osg::Vec3f focal, cameraPos;

@ -39,11 +39,11 @@ namespace
{ {
std::ostringstream texname; std::ostringstream texname;
texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds"; texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds";
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(resourceSystem->getImageManager()->getImage(texname.str()))); osg::ref_ptr<osg::Texture2D> tex2 (new osg::Texture2D(resourceSystem->getImageManager()->getImage(texname.str())));
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); tex2->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); tex2->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
resourceSystem->getSceneManager()->applyFilterSettings(tex); resourceSystem->getSceneManager()->applyFilterSettings(tex2);
textures.push_back(tex); textures.push_back(tex2);
} }
osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures)); osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures));
@ -117,6 +117,7 @@ RippleSimulation::~RippleSimulation()
void RippleSimulation::update(float dt) void RippleSimulation::update(float dt)
{ {
const MWBase::World* world = MWBase::Environment::get().getWorld();
for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it) for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it)
{ {
if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr()) if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr())
@ -128,10 +129,8 @@ void RippleSimulation::update(float dt)
osg::Vec3f currentPos (it->mPtr.getRefData().getPosition().asVec3()); osg::Vec3f currentPos (it->mPtr.getRefData().getPosition().asVec3());
if ( (currentPos - it->mLastEmitPosition).length() > 10 bool shouldEmit = ( world->isUnderwater (it->mPtr.getCell(), it->mPtr.getRefData().getPosition().asVec3()) && !world->isSubmerged(it->mPtr) ) || world->isWalkingOnWater(it->mPtr);
// Only emit when close to the water surface, not above it and not too deep in the water if ( shouldEmit && (currentPos - it->mLastEmitPosition).length() > 10 )
&& MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(), it->mPtr.getRefData().getPosition().asVec3())
&& !MWBase::Environment::get().getWorld()->isSubmerged(it->mPtr))
{ {
it->mLastEmitPosition = currentPos; it->mLastEmitPosition = currentPos;

@ -1329,7 +1329,7 @@ void SkyManager::createRain()
mRainParticleSystem = new osgParticle::ParticleSystem; mRainParticleSystem = new osgParticle::ParticleSystem;
mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED); mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0)); mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0));
mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,-1)); mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,1));
osg::ref_ptr<osg::StateSet> stateset (mRainParticleSystem->getOrCreateStateSet()); osg::ref_ptr<osg::StateSet> stateset (mRainParticleSystem->getOrCreateStateSet());

@ -238,15 +238,15 @@ namespace MWScript
else else
{ {
char type = declarations.getType (iter->first); char type = declarations.getType (iter->first);
char index = declarations.getIndex (iter->first); int index2 = declarations.getIndex (iter->first);
try try
{ {
switch (type) switch (type)
{ {
case 's': mShorts.at (index) = iter->second.getInteger(); break; case 's': mShorts.at (index2) = iter->second.getInteger(); break;
case 'l': mLongs.at (index) = iter->second.getInteger(); break; case 'l': mLongs.at (index2) = iter->second.getInteger(); break;
case 'f': mFloats.at (index) = iter->second.getFloat(); break; case 'f': mFloats.at (index2) = iter->second.getFloat(); break;
// silently ignore locals that don't exist anymore // silently ignore locals that don't exist anymore
} }

@ -34,8 +34,9 @@
namespace MWSound namespace MWSound
{ {
SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound) SoundManager::SoundManager(const VFS::Manager* vfs, const std::map<std::string,std::string>& fallbackMap, bool useSound)
: mVFS(vfs) : mVFS(vfs)
, mFallback(fallbackMap)
, mOutput(new DEFAULT_OUTPUT(*this)) , mOutput(new DEFAULT_OUTPUT(*this))
, mMasterVolume(1.0f) , mMasterVolume(1.0f)
, mSFXVolume(1.0f) , mSFXVolume(1.0f)
@ -61,6 +62,13 @@ namespace MWSound
mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound");
mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f); mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f);
mNearWaterRadius = mFallback.getFallbackInt("Water_NearWaterRadius");
mNearWaterPoints = mFallback.getFallbackInt("Water_NearWaterPoints");
mNearWaterIndoorTolerance = mFallback.getFallbackFloat("Water_NearWaterIndoorTolerance");
mNearWaterOutdoorTolerance = mFallback.getFallbackFloat("Water_NearWaterOutdoorTolerance");
mNearWaterIndoorID = mFallback.getFallbackString("Water_NearWaterIndoorID");
mNearWaterOutdoorID = mFallback.getFallbackString("Water_NearWaterOutdoorID");
mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1); mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1);
mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1); mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1);
mBufferCacheMax *= 1024*1024; mBufferCacheMax *= 1024*1024;
@ -796,6 +804,96 @@ namespace MWSound
} }
} }
void SoundManager::updateWaterSound(float /*duration*/)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::ConstPtr player = world->getPlayerPtr();
osg::Vec3f pos = player.getRefData().getPosition().asVec3();
float volume = 0.0f;
const std::string& soundId = player.getCell()->isExterior() ? mNearWaterOutdoorID : mNearWaterIndoorID;
if (!mListenerUnderwater)
{
if (player.getCell()->getCell()->hasWater())
{
float dist = std::abs(player.getCell()->getWaterLevel() - pos.z());
if (player.getCell()->isExterior() && dist < mNearWaterOutdoorTolerance)
{
volume = (mNearWaterOutdoorTolerance - dist) / mNearWaterOutdoorTolerance;
if (mNearWaterPoints > 1)
{
int underwaterPoints = 0;
float step = mNearWaterRadius * 2.0f / (mNearWaterPoints - 1);
for (int x = 0; x < mNearWaterPoints; x++)
{
for (int y = 0; y < mNearWaterPoints; y++)
{
float height = world->getTerrainHeightAt(
osg::Vec3f(pos.x() - mNearWaterRadius + x*step, pos.y() - mNearWaterRadius + y*step, 0.0f));
if (height < 0)
underwaterPoints++;
}
}
volume *= underwaterPoints * 2.0f / (mNearWaterPoints*mNearWaterPoints);
}
}
else if (!player.getCell()->isExterior() && dist < mNearWaterIndoorTolerance)
{
volume = (mNearWaterIndoorTolerance - dist) / mNearWaterIndoorTolerance;
}
}
}
else
volume = 1.0f;
volume = std::min(volume, 1.0f);
if (mNearWaterSound)
{
if (volume == 0.0f)
{
mOutput->finishSound(mNearWaterSound);
mNearWaterSound.reset();
}
else
{
bool soundIdChanged = false;
Sound_Buffer* sfx = lookupSound(Misc::StringUtils::lowerCase(soundId));
for (SoundMap::const_iterator snditer = mActiveSounds.begin(); snditer != mActiveSounds.end(); ++snditer)
{
for (SoundBufferRefPairList::const_iterator pairiter = snditer->second.begin(); pairiter != snditer->second.end(); ++pairiter)
{
if (pairiter->first == mNearWaterSound)
{
if (pairiter->second != sfx)
soundIdChanged = true;
break;
}
}
}
if (soundIdChanged)
{
mOutput->finishSound(mNearWaterSound);
mNearWaterSound = playSound(soundId, volume, 1.0f, Play_TypeSfx, Play_Loop);
}
else if (sfx)
mNearWaterSound->setVolume(volume * sfx->mVolume);
}
}
else if (volume > 0.0f)
mNearWaterSound = playSound(soundId, volume, 1.0f, Play_TypeSfx, Play_Loop);
}
void SoundManager::updateSounds(float duration) void SoundManager::updateSounds(float duration)
{ {
static float timePassed = 0.0; static float timePassed = 0.0;
@ -941,6 +1039,7 @@ namespace MWSound
{ {
updateSounds(duration); updateSounds(duration);
updateRegionSound(duration); updateRegionSound(duration);
updateWaterSound(duration);
} }
} }
@ -1105,6 +1204,7 @@ namespace MWSound
mOutput->finishStream(*trkiter); mOutput->finishStream(*trkiter);
mActiveTracks.clear(); mActiveTracks.clear();
mUnderwaterSound.reset(); mUnderwaterSound.reset();
mNearWaterSound.reset();
stopMusic(); stopMusic();
} }
} }

@ -11,6 +11,8 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/fallback/fallback.hpp>
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
namespace VFS namespace VFS
@ -44,6 +46,8 @@ namespace MWSound
{ {
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
Fallback::Map mFallback;
std::auto_ptr<Sound_Output> mOutput; std::auto_ptr<Sound_Output> mOutput;
// Caches available music tracks by <playlist name, (sound files) > // Caches available music tracks by <playlist name, (sound files) >
@ -56,6 +60,13 @@ namespace MWSound
float mVoiceVolume; float mVoiceVolume;
float mFootstepsVolume; float mFootstepsVolume;
int mNearWaterRadius;
int mNearWaterPoints;
float mNearWaterIndoorTolerance;
float mNearWaterOutdoorTolerance;
std::string mNearWaterIndoorID;
std::string mNearWaterOutdoorID;
typedef std::auto_ptr<std::deque<Sound_Buffer> > SoundBufferList; typedef std::auto_ptr<std::deque<Sound_Buffer> > SoundBufferList;
// List of sound buffers, grown as needed. New enties are added to the // List of sound buffers, grown as needed. New enties are added to the
// back, allowing existing Sound_Buffer references/pointers to remain // back, allowing existing Sound_Buffer references/pointers to remain
@ -94,6 +105,7 @@ namespace MWSound
int mPausedSoundTypes; int mPausedSoundTypes;
MWBase::SoundPtr mUnderwaterSound; MWBase::SoundPtr mUnderwaterSound;
MWBase::SoundPtr mNearWaterSound;
Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound);
@ -108,6 +120,7 @@ namespace MWSound
void streamMusicFull(const std::string& filename); void streamMusicFull(const std::string& filename);
void updateSounds(float duration); void updateSounds(float duration);
void updateRegionSound(float duration); void updateRegionSound(float duration);
void updateWaterSound(float duration);
float volumeFromType(PlayType type) const; float volumeFromType(PlayType type) const;
@ -119,7 +132,7 @@ namespace MWSound
friend class OpenAL_Output; friend class OpenAL_Output;
public: public:
SoundManager(const VFS::Manager* vfs, bool useSound); SoundManager(const VFS::Manager* vfs, const std::map<std::string, std::string>& fallbackMap, bool useSound);
virtual ~SoundManager(); virtual ~SoundManager();
virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual void processChangedSettings(const Settings::CategorySettingVector& settings);

@ -249,7 +249,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
+MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords()
+MWBase::Environment::get().getWindowManager()->countSavedGameRecords() +MWBase::Environment::get().getWindowManager()->countSavedGameRecords()
+MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords(); +MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords()
+MWBase::Environment::get().getInputManager()->countSavedGameRecords();
writer.setRecordCount (recordCount); writer.setRecordCount (recordCount);
writer.save (stream); writer.save (stream);
@ -271,6 +272,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener); MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener);
MWBase::Environment::get().getWindowManager()->write(writer, listener); MWBase::Environment::get().getWindowManager()->write(writer, listener);
MWBase::Environment::get().getMechanicsManager()->write(writer, listener); MWBase::Environment::get().getMechanicsManager()->write(writer, listener);
MWBase::Environment::get().getInputManager()->write(writer, listener);
// Ensure we have written the number of records that was estimated // Ensure we have written the number of records that was estimated
if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record
@ -462,6 +464,10 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.intval); MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.intval);
break; break;
case ESM::REC_INPU:
MWBase::Environment::get().getInputManager()->readRecord(reader, n.intval);
break;
default: default:
// ignore invalid records // ignore invalid records

@ -93,6 +93,24 @@ namespace MWWorld
} }
} }
void CellRef::applyChargeRemainderToBeSubtracted(float chargeRemainder)
{
mCellRef.mChargeIntRemainder += std::abs(chargeRemainder);
if (mCellRef.mChargeIntRemainder > 1.0f)
{
float newChargeRemainder = (mCellRef.mChargeIntRemainder - std::floor(mCellRef.mChargeIntRemainder));
if (mCellRef.mChargeInt <= static_cast<int>(mCellRef.mChargeIntRemainder))
{
mCellRef.mChargeInt = 0;
}
else
{
mCellRef.mChargeInt -= static_cast<int>(mCellRef.mChargeIntRemainder);
}
mCellRef.mChargeIntRemainder = newChargeRemainder;
}
}
float CellRef::getChargeFloat() const float CellRef::getChargeFloat() const
{ {
return mCellRef.mChargeFloat; return mCellRef.mChargeFloat;

@ -65,6 +65,7 @@ namespace MWWorld
float getChargeFloat() const; // Implemented as union with int charge float getChargeFloat() const; // Implemented as union with int charge
void setCharge(int charge); void setCharge(int charge);
void setChargeFloat(float charge); void setChargeFloat(float charge);
void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1
// The NPC that owns this object (and will get angry if you steal it) // The NPC that owns this object (and will get angry if you steal it)
std::string getOwner() const; std::string getOwner() const;

@ -204,6 +204,14 @@ namespace MWWorld
return (ref.mRef.mRefnum == pRefnum); return (ref.mRef.mRefnum == pRefnum);
} }
Ptr CellStore::getCurrentPtr(LiveCellRefBase *ref)
{
MovedRefTracker::iterator found = mMovedToAnotherCell.find(ref);
if (found != mMovedToAnotherCell.end())
return Ptr(ref, found->second);
return Ptr(ref, this);
}
void CellStore::moveFrom(const Ptr &object, CellStore *from) void CellStore::moveFrom(const Ptr &object, CellStore *from)
{ {
if (mState != State_Loaded) if (mState != State_Loaded)
@ -964,26 +972,26 @@ namespace MWWorld
mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
{ {
Ptr ptr (&*it, this); Ptr ptr = getCurrentPtr(&*it);
ptr.getClass().respawn(ptr); ptr.getClass().respawn(ptr);
} }
} }
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
{ {
Ptr ptr (&*it, this); Ptr ptr = getCurrentPtr(&*it);
clearCorpse(ptr); clearCorpse(ptr);
ptr.getClass().respawn(ptr); ptr.getClass().respawn(ptr);
} }
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
{ {
Ptr ptr (&*it, this); Ptr ptr = getCurrentPtr(&*it);
clearCorpse(ptr); clearCorpse(ptr);
ptr.getClass().respawn(ptr); ptr.getClass().respawn(ptr);
} }
for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it) for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)
{ {
Ptr ptr (&*it, this); Ptr ptr = getCurrentPtr(&*it);
// no need to clearCorpse, handled as part of mCreatures // no need to clearCorpse, handled as part of mCreatures
ptr.getClass().respawn(ptr); ptr.getClass().respawn(ptr);
} }

@ -111,6 +111,9 @@ namespace MWWorld
// Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell
std::vector<LiveCellRefBase*> mMergedRefs; std::vector<LiveCellRefBase*> mMergedRefs;
// Get the Ptr for the given ref which originated from this cell (possibly moved to another cell at this point).
Ptr getCurrentPtr(MWWorld::LiveCellRefBase* ref);
/// Moves object from the given cell to this cell. /// Moves object from the given cell to this cell.
void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from); void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from);

@ -393,7 +393,26 @@ namespace MWWorld
bool Class::isPureWaterCreature(const MWWorld::Ptr& ptr) const bool Class::isPureWaterCreature(const MWWorld::Ptr& ptr) const
{ {
return canSwim(ptr) && !canWalk(ptr); return canSwim(ptr)
&& !isBipedal(ptr)
&& !canFly(ptr)
&& !canWalk(ptr);
}
bool Class::isPureFlyingCreature(const Ptr& ptr) const
{
return canFly(ptr)
&& !isBipedal(ptr)
&& !canSwim(ptr)
&& !canWalk(ptr);
}
bool Class::isPureLandCreature(const Ptr& ptr) const
{
return canWalk(ptr)
&& !isBipedal(ptr)
&& !canFly(ptr)
&& !canSwim(ptr);
} }
bool Class::isMobile(const MWWorld::Ptr& ptr) const bool Class::isMobile(const MWWorld::Ptr& ptr) const

@ -312,6 +312,8 @@ namespace MWWorld
virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; virtual bool canSwim(const MWWorld::ConstPtr& ptr) const;
virtual bool canWalk(const MWWorld::ConstPtr& ptr) const; virtual bool canWalk(const MWWorld::ConstPtr& ptr) const;
bool isPureWaterCreature(const MWWorld::Ptr& ptr) const; bool isPureWaterCreature(const MWWorld::Ptr& ptr) const;
bool isPureFlyingCreature(const MWWorld::Ptr& ptr) const;
bool isPureLandCreature(const MWWorld::Ptr& ptr) const;
bool isMobile(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;

@ -553,14 +553,14 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW
if(listInMap != allowedForReplace.end()) if(listInMap != allowedForReplace.end())
restockNum -= std::min(restockNum, listInMap->second); restockNum -= std::min(restockNum, listInMap->second);
//restock //restock
addInitialItem(itemOrList, owner, restockNum, true); addInitialItem(itemOrList, owner, -restockNum, true);
} }
else else
{ {
//Restocking static item - just restock to the max count //Restocking static item - just restock to the max count
int currentCount = count(itemOrList); int currentCount = count(itemOrList);
if (currentCount < std::abs(it->mCount)) if (currentCount < std::abs(it->mCount))
addInitialItem(itemOrList, owner, std::abs(it->mCount) - currentCount, true); addInitialItem(itemOrList, owner, -(std::abs(it->mCount) - currentCount), true);
} }
} }
flagAsModified(); flagAsModified();

@ -6,6 +6,7 @@
#include <components/esm/loadench.hpp> #include <components/esm/loadench.hpp>
#include <components/esm/inventorystate.hpp> #include <components/esm/inventorystate.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -214,36 +215,71 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
return mSlots[slot]; return mSlots[slot];
} }
bool MWWorld::InventoryStore::canActorAutoEquip(const MWWorld::Ptr& actor, const MWWorld::Ptr& item)
{
if (!Settings::Manager::getBool("prevent merchant equipping", "Game"))
return true;
// Only autoEquip if we are the original owner of the item.
// This stops merchants from auto equipping anything you sell to them.
// ...unless this is a companion, he should always equip items given to him.
if (!Misc::StringUtils::ciEqual(item.getCellRef().getOwner(), actor.getCellRef().getRefId()) &&
(actor.getClass().getScript(actor).empty() ||
!actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))
&& !actor.getClass().getCreatureStats(actor).isDead() // Corpses can be dressed up by the player as desired
)
{
return false;
}
return true;
}
void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
{ {
if (!actor.getClass().isNpc())
// autoEquip is no-op for creatures
return;
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor);
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat();
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat();
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
float unarmoredRating = static_cast<int>((fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill));
TSlots slots_; TSlots slots_;
initSlots (slots_); initSlots (slots_);
// Disable model update during auto-equip // Disable model update during auto-equip
mUpdatesEnabled = false; mUpdatesEnabled = false;
for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) // Autoequip clothing, armor and weapons.
// Equipping lights is handled in Actors::updateEquippedLight based on environment light.
for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter)
{ {
Ptr test = *iter; Ptr test = *iter;
// Don't autoEquip lights. Handled in Actors::updateEquippedLight based on environment light. if (!canActorAutoEquip(actor, test))
if (test.getTypeName() == typeid(ESM::Light).name()) continue;
switch(test.getClass().canBeEquipped (test, actor).first)
{ {
case 0:
continue; continue;
default:
break;
} }
// Only autoEquip if we are the original owner of the item. if (iter.getType() == ContainerStore::Type_Armor &&
// This stops merchants from auto equipping anything you sell to them. test.getClass().getEffectiveArmorRating(test, actor) <= std::max(unarmoredRating, 0.f))
// ...unless this is a companion, he should always equip items given to him.
if (!Misc::StringUtils::ciEqual(test.getCellRef().getOwner(), actor.getCellRef().getRefId()) &&
(actor.getClass().getScript(actor).empty() ||
!actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))
&& !actor.getClass().getCreatureStats(actor).isDead() // Corpses can be dressed up by the player as desired
)
{ {
continue; continue;
} }
int testSkill = test.getClass().getEquipmentSkill (test);
std::pair<std::vector<int>, bool> itemsSlots = std::pair<std::vector<int>, bool> itemsSlots =
iter->getClass().getEquipmentSlots (*iter); iter->getClass().getEquipmentSlots (*iter);
@ -251,49 +287,33 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin()); for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin());
iter2!=itemsSlots.first.end(); ++iter2) iter2!=itemsSlots.first.end(); ++iter2)
{ {
if (*iter2 == Slot_CarriedRight) // Items in right hand are situational use, so don't equip them.
// Equipping weapons is handled by AiCombat. Anything else (lockpicks, probes) can't be used by NPCs anyway (yet)
continue;
if (iter.getType() == MWWorld::ContainerStore::Type_Weapon)
continue;
if (slots_.at (*iter2)!=end()) if (slots_.at (*iter2)!=end())
{ {
Ptr old = *slots_.at (*iter2); Ptr old = *slots_.at (*iter2);
// check skill if (iter.getType() == ContainerStore::Type_Armor)
int oldSkill = old.getClass().getEquipmentSkill (old);
bool use = false;
if (testSkill!=-1 && oldSkill==-1)
use = true;
else if (testSkill!=-1 && oldSkill!=-1 && testSkill!=oldSkill)
{ {
if (actor.getClass().getSkill(actor, oldSkill) > actor.getClass().getSkill (actor, testSkill)) if (old.getTypeName() == typeid(ESM::Armor).name())
continue; // rejected, because old item better matched the NPC's skills.
if (actor.getClass().getSkill(actor, oldSkill) < actor.getClass().getSkill (actor, testSkill))
use = true;
}
if (!use)
{
// check value
if (old.getClass().getValue (old)>=
test.getClass().getValue (test))
{ {
if (old.getClass().getEffectiveArmorRating(old, actor) >= test.getClass().getEffectiveArmorRating(test, actor))
// old armor had better armor rating
continue; continue;
} }
// suitable armor should replace already equipped clothing
} }
} else if (iter.getType() == ContainerStore::Type_Clothing)
switch(test.getClass().canBeEquipped (test, actor).first)
{ {
case 0: if (old.getTypeName() == typeid(ESM::Clothing).name())
{
// check value
if (old.getClass().getValue (old) > test.getClass().getValue (test))
// old clothing was more valuable
continue; continue;
default: }
break; else
// suitable clothing should NOT replace already equipped armor
continue;
}
} }
if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped
@ -310,6 +330,99 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
} }
} }
static const ESM::Skill::SkillEnum weaponSkills[] =
{
ESM::Skill::LongBlade,
ESM::Skill::Axe,
ESM::Skill::Spear,
ESM::Skill::ShortBlade,
ESM::Skill::Marksman,
ESM::Skill::BluntWeapon
};
const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]);
bool weaponSkillVisited[weaponSkillsLength] = { false };
for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i)
{
int max = 0;
int maxWeaponSkill = -1;
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
{
int skillValue = stats.getSkill(static_cast<int>(weaponSkills[j])).getModified();
if (skillValue > max && !weaponSkillVisited[j])
{
max = skillValue;
maxWeaponSkill = j;
}
}
if (maxWeaponSkill == -1)
break;
max = 0;
ContainerStoreIterator weapon(end());
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter)
{
if (!canActorAutoEquip(actor, *iter))
continue;
const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase;
if (esmWeapon->mData.mType == ESM::Weapon::Arrow || esmWeapon->mData.mType == ESM::Weapon::Bolt)
continue;
if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill])
{
if (esmWeapon->mData.mChop[1] >= max)
{
max = esmWeapon->mData.mChop[1];
weapon = iter;
}
if (esmWeapon->mData.mSlash[1] >= max)
{
max = esmWeapon->mData.mSlash[1];
weapon = iter;
}
if (esmWeapon->mData.mThrust[1] >= max)
{
max = esmWeapon->mData.mThrust[1];
weapon = iter;
}
}
}
if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first)
{
std::pair<std::vector<int>, bool> itemsSlots =
weapon->getClass().getEquipmentSlots (*weapon);
for (std::vector<int>::const_iterator slot (itemsSlots.first.begin());
slot!=itemsSlots.first.end(); ++slot)
{
if (!itemsSlots.second)
{
if (weapon->getRefData().getCount() > 1)
{
unstack(*weapon, actor);
}
}
slots_[*slot] = weapon;
break;
}
break;
}
weaponSkillVisited[maxWeaponSkill] = true;
}
bool changed = false; bool changed = false;
for (std::size_t i=0; i<slots_.size(); ++i) for (std::size_t i=0; i<slots_.size(); ++i)
@ -402,8 +515,8 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effectIt->mEffectID); effectIt->mEffectID);
// Fully resisted? // Fully resisted or can't be applied to target?
if (params[i].mMultiplier == 0) if (params[i].mMultiplier == 0 || !MWMechanics::checkEffectTarget(effectIt->mEffectID, actor, actor, actor == MWMechanics::getPlayer()))
continue; continue;
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;
@ -657,6 +770,9 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
effectIt!=enchantment.mEffects.mList.end(); ++effectIt) effectIt!=enchantment.mEffects.mList.end(); ++effectIt)
{ {
// Don't get spell icon display information for enchantments that weren't actually applied
if (mMagicEffects.get(MWMechanics::EffectKey(*effectIt)).getMagnitude() == 0)
continue;
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i]; const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i];
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
magnitude *= params.mMultiplier; magnitude *= params.mMultiplier;

@ -115,6 +115,8 @@ namespace MWWorld
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory); virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory);
bool canActorAutoEquip(const MWWorld::Ptr& actor, const MWWorld::Ptr& item);
public: public:
InventoryStore(); InventoryStore();

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save