1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 19:19:56 +00:00
# Conflicts:
#	docs/source/openmw-mods/index.rst
This commit is contained in:
Ryan Tucker 2016-12-19 21:06:36 -08:00
commit bf805813e5
145 changed files with 2968 additions and 933 deletions

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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" \

View file

@ -46,7 +46,7 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
message(STATUS "Shallow Git clone detected, not attempting to retrieve version info") message(STATUS "Shallow Git clone detected, not attempting to retrieve version info")
endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
# Macros # Macros
include(OpenMWMacros) include(OpenMWMacros)
@ -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")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local if (NOT APPLE)
"${OpenMW_BINARY_DIR}/openmw.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${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 ()

View file

@ -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)

View file

@ -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

View file

@ -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;
} }
} }

View file

@ -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);
} }

View file

@ -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);
} }

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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())

View file

@ -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")

View file

@ -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()

View file

@ -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

View file

@ -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
}; };

View file

@ -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;

View file

@ -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:

View file

@ -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] =

View file

@ -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);

View file

@ -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

View file

@ -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
} }

View file

@ -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&)));

View file

@ -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));

View file

@ -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());
} }

View file

@ -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.

View file

@ -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)

View file

@ -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)
{ {

View file

@ -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));

View file

@ -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;
}; };
} }

View file

@ -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;

View file

@ -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;
}; };
} }

View file

@ -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;
} }

View file

@ -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;

View file

@ -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;

View file

@ -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));

View file

@ -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

View file

@ -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;

View file

@ -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;
} }

View file

@ -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)));
}
}

View file

@ -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

View file

@ -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;

View file

@ -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) Gui::Box* box = dynamic_cast<Gui::Box*>(mMainWidget);
{ if (box == NULL)
// shrink throw std::runtime_error("main widget must be a box");
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()) box->notifyChildrenSizeChanged();
MyGUI::Gui::getInstance().destroyWidget(mView->getChildAt(0)); center();
int currentY = 0;
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
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)));
}
} }

View file

@ -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);

View file

@ -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) Gui::Box* box = dynamic_cast<Gui::Box*>(mMainWidget);
{ if (box == NULL)
// shrink throw std::runtime_error("main widget must be a box");
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()) box->notifyChildrenSizeChanged();
MyGUI::Gui::getInstance().destroyWidget(mRepairView->getChildAt(0)); center();
int currentY = 0;
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
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)));
}
} }

View file

@ -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);
}; };

View file

@ -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();
updateSkillArea(); mUpdateSkillArea = true;
}
void ReviewDialog::onFrame(float /*duration*/)
{
if (mUpdateSkillArea)
{
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;
attr->second->setAttributeValue(value); if (attr->second->getAttributeValue() != 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));

View file

@ -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

View file

@ -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()

View file

@ -66,6 +66,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();

View file

@ -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;
} }

View file

@ -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:

View file

@ -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}");
} }

View file

@ -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)
{ {

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -512,8 +512,8 @@ 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);

View file

@ -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;
} }

View file

@ -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
{ {
@ -50,23 +51,40 @@ namespace MWMechanics
bool mForceNoShortcut; bool mForceNoShortcut;
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); {
if (is_target_reached) storage.mReadyToAttack = true; //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;
}
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,30 +225,61 @@ 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;
if (actionCooldown > 0)
return;
float &rangeAttack = storage.mAttackRange;
boost::shared_ptr<Action>& currentAction = storage.mCurrentAction; boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
if (characterController.readyToPrepareAttack())
if (!forceFlee)
{ {
currentAction = prepareNextAction(actor, target); if (actionCooldown > 0)
return;
if (characterController.readyToPrepareAttack())
{
currentAction = prepareNextAction(actor, target);
actionCooldown = currentAction->getActionCooldown();
}
}
else
{
currentAction.reset(new ActionFlee());
actionCooldown = currentAction->getActionCooldown(); actionCooldown = currentAction->getActionCooldown();
} }
const ESM::Weapon *weapon = NULL; if (!currentAction)
bool isRangedCombat = false; return;
if (currentAction.get())
if (storage.isFleeing() != currentAction->isFleeing())
{ {
rangeAttack = currentAction->getCombatRange(isRangedCombat); if (currentAction->isFleeing())
// Get weapon characteristics {
weapon = currentAction->getWeapon(); storage.startFleeing();
MWBase::Environment::get().getDialogueManager()->say(actor, "flee");
return;
}
else
storage.stopFleeing();
} }
bool isRangedCombat = false;
float &rangeAttack = storage.mAttackRange;
rangeAttack = currentAction->getCombatRange(isRangedCombat);
// Get weapon characteristics
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());
osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3()); osg::Vec3f vTargetPos(target.getRefData().getPosition().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;
}
} }

View file

@ -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,

View file

@ -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;
}
} }

View file

@ -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

View file

@ -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
} }

View file

@ -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;

View file

@ -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);

View file

@ -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");

View file

@ -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);

View file

@ -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);
}
} }

View file

@ -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

View file

@ -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);
MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor);
if(werewolf) if(werewolf)
{ {
MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor); 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())

View file

@ -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);

View file

@ -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;
} }
} }

View file

@ -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
{ {

View file

@ -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); const osg::Vec3f actorPosition = physicActor->getPosition();
if (foundActor == mActors.end()) // actor was already removed from the scene physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel));
continue; }
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)

View file

@ -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;

View file

@ -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;
} }
} }

View file

@ -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

View file

@ -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>();

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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());

View file

@ -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
} }

View file

@ -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();
} }
} }

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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);
} }

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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();

View file

@ -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; continue;
switch(test.getClass().canBeEquipped (test, actor).first)
{
case 0:
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))
{ {
continue; if (old.getClass().getEffectiveArmorRating(old, actor) >= test.getClass().getEffectiveArmorRating(test, actor))
// old armor had better armor rating
continue;
} }
// suitable armor should replace already equipped clothing
}
else if (iter.getType() == ContainerStore::Type_Clothing)
{
if (old.getTypeName() == typeid(ESM::Clothing).name())
{
// check value
if (old.getClass().getValue (old) > test.getClass().getValue (test))
// old clothing was more valuable
continue;
}
else
// suitable clothing should NOT replace already equipped armor
continue;
} }
}
switch(test.getClass().canBeEquipped (test, actor).first)
{
case 0:
continue;
default:
break;
} }
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;

View file

@ -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