mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-29 03:26:42 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/OpenMW/openmw
# Conflicts: # docs/source/openmw-mods/index.rst
This commit is contained in:
		
						commit
						bf805813e5
					
				
					 145 changed files with 2968 additions and 933 deletions
				
			
		|  | @ -1,6 +1,6 @@ | ||||||
| os: | os: | ||||||
|  - linux |  - linux | ||||||
|  - osx | # - osx | ||||||
| osx_image: xcode7.2 | osx_image: xcode7.2 | ||||||
| language: cpp | language: cpp | ||||||
| sudo: required | sudo: required | ||||||
|  |  | ||||||
|  | @ -23,10 +23,12 @@ Programmers | ||||||
|     Alexander Olofsson (Ace) |     Alexander Olofsson (Ace) | ||||||
|     Allofich |     Allofich | ||||||
|     AnyOldName3 |     AnyOldName3 | ||||||
|  |     Aussiemon | ||||||
|     Austin Salgat (Salgat) |     Austin Salgat (Salgat) | ||||||
|     Artem Kotsynyak (greye) |     Artem Kotsynyak (greye) | ||||||
|     artemutin |     artemutin | ||||||
|     Arthur Moore (EmperorArthur) |     Arthur Moore (EmperorArthur) | ||||||
|  |     Assumeru | ||||||
|     athile |     athile | ||||||
|     Ben Shealy (bentsherman) |     Ben Shealy (bentsherman) | ||||||
|     Bret Curtis (psi29a) |     Bret Curtis (psi29a) | ||||||
|  | @ -73,6 +75,7 @@ Programmers | ||||||
|     Karl-Felix Glatzer (k1ll) |     Karl-Felix Glatzer (k1ll) | ||||||
|     Kevin Poitra (PuppyKevin) |     Kevin Poitra (PuppyKevin) | ||||||
|     Koncord |     Koncord | ||||||
|  |     Kurnevsky Evgeny (kurnevsky) | ||||||
|     Lars Söderberg (Lazaroth) |     Lars Söderberg (Lazaroth) | ||||||
|     lazydev |     lazydev | ||||||
|     Leon Saunders (emoose) |     Leon Saunders (emoose) | ||||||
|  | @ -119,6 +122,7 @@ Programmers | ||||||
|     Scott Howard |     Scott Howard | ||||||
|     Sebastian Wick (swick) |     Sebastian Wick (swick) | ||||||
|     Sergey Shambir |     Sergey Shambir | ||||||
|  |     ShadowRadiance | ||||||
|     sir_herrbatka |     sir_herrbatka | ||||||
|     smbas |     smbas | ||||||
|     Stefan Galowicz (bogglez) |     Stefan Galowicz (bogglez) | ||||||
|  |  | ||||||
|  | @ -1,10 +1,11 @@ | ||||||
| #!/bin/sh | #!/bin/sh | ||||||
| 
 | 
 | ||||||
| brew update | brew update | ||||||
|  | 
 | ||||||
| brew rm cmake || true | brew rm cmake || true | ||||||
| brew rm pkgconfig || true | brew rm pkgconfig || true | ||||||
| brew rm qt5 || true | brew rm qt5 || true | ||||||
| brew install cmake pkgconfig qt5 | brew install cmake pkgconfig qt55 | ||||||
| 
 | 
 | ||||||
| curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip | curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip | ||||||
| unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null | unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null | ||||||
|  |  | ||||||
|  | @ -4,13 +4,12 @@ export CXX=clang++ | ||||||
| export CC=clang | export CC=clang | ||||||
| 
 | 
 | ||||||
| DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" | DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" | ||||||
| QT_PATH="/usr/local/opt/qt5" | QT_PATH="/usr/local/opt/qt55" | ||||||
| 
 | 
 | ||||||
| mkdir build | mkdir build | ||||||
| cd build | cd build | ||||||
| 
 | 
 | ||||||
| cmake \ | cmake \ | ||||||
| -D CMAKE_EXE_LINKER_FLAGS="-lz" \ |  | ||||||
| -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ | -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ | ||||||
| -D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \ | -D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \ | ||||||
| -D CMAKE_OSX_SYSROOT="macosx10.11" \ | -D CMAKE_OSX_SYSROOT="macosx10.11" \ | ||||||
|  |  | ||||||
|  | @ -96,7 +96,7 @@ endif() | ||||||
| # Set up common paths | # Set up common paths | ||||||
| if (APPLE) | if (APPLE) | ||||||
|     set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") |     set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") | ||||||
|     set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") |     set(OPENMW_RESOURCE_FILES "../Resources/resources" CACHE PATH "location of OpenMW resources files") | ||||||
| elseif(UNIX) | elseif(UNIX) | ||||||
|     # Paths |     # Paths | ||||||
|     SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") |     SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") | ||||||
|  | @ -286,6 +286,11 @@ endif (APPLE) | ||||||
| # Set up DEBUG define | # Set up DEBUG define | ||||||
| set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) | set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) | ||||||
| 
 | 
 | ||||||
|  | if (NOT APPLE) | ||||||
|  |     set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR}) | ||||||
|  |     set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR}) | ||||||
|  | endif () | ||||||
|  | 
 | ||||||
| add_subdirectory(files/) | add_subdirectory(files/) | ||||||
| 
 | 
 | ||||||
| # Specify build paths | # Specify build paths | ||||||
|  | @ -307,11 +312,15 @@ endif (APPLE) | ||||||
| configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg | configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg | ||||||
|     "${OpenMW_BINARY_DIR}/settings-default.cfg") |     "${OpenMW_BINARY_DIR}/settings-default.cfg") | ||||||
| 
 | 
 | ||||||
|  | if (NOT APPLE) | ||||||
|     configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local |     configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local | ||||||
|         "${OpenMW_BINARY_DIR}/openmw.cfg") |         "${OpenMW_BINARY_DIR}/openmw.cfg") | ||||||
| 
 |  | ||||||
|     configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg |     configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg | ||||||
|         "${OpenMW_BINARY_DIR}/openmw.cfg.install") |         "${OpenMW_BINARY_DIR}/openmw.cfg.install") | ||||||
|  | else () | ||||||
|  |     configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg | ||||||
|  |         "${OpenMW_BINARY_DIR}/openmw.cfg") | ||||||
|  | endif () | ||||||
| 
 | 
 | ||||||
| configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg | configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg | ||||||
|     "${OpenMW_BINARY_DIR}/openmw-cs.cfg") |     "${OpenMW_BINARY_DIR}/openmw-cs.cfg") | ||||||
|  | @ -419,8 +428,10 @@ IF(NOT WIN32 AND NOT APPLE) | ||||||
| ENDIF(NOT WIN32 AND NOT APPLE) | ENDIF(NOT WIN32 AND NOT APPLE) | ||||||
| 
 | 
 | ||||||
| if(WIN32) | if(WIN32) | ||||||
|     FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll") |     FILE(GLOB dll_files_debug "${OpenMW_BINARY_DIR}/Debug/*.dll") | ||||||
|     INSTALL(FILES ${dll_files} DESTINATION ".") |     FILE(GLOB dll_files_release "${OpenMW_BINARY_DIR}/Release/*.dll") | ||||||
|  |     INSTALL(FILES ${dll_files_debug} DESTINATION "." CONFIGURATIONS Debug) | ||||||
|  |     INSTALL(FILES ${dll_files_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) | ||||||
|     INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") |     INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") | ||||||
|     INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt") |     INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt") | ||||||
|     INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") |     INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") | ||||||
|  | @ -429,36 +440,23 @@ if(WIN32) | ||||||
|         "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt" |         "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt" | ||||||
|         "${OpenMW_BINARY_DIR}/settings-default.cfg" |         "${OpenMW_BINARY_DIR}/settings-default.cfg" | ||||||
|         "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" |         "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" | ||||||
|         "${OpenMW_BINARY_DIR}/Release/openmw.exe" |  | ||||||
|         DESTINATION ".") |         DESTINATION ".") | ||||||
| 
 | 
 | ||||||
|     IF(BUILD_LAUNCHER) |  | ||||||
|         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-launcher.exe" DESTINATION ".") |  | ||||||
|     ENDIF(BUILD_LAUNCHER) |  | ||||||
|     IF(BUILD_MWINIIMPORTER) |  | ||||||
|         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-iniimporter.exe" DESTINATION ".") |  | ||||||
|     ENDIF(BUILD_MWINIIMPORTER) |  | ||||||
|     IF(BUILD_ESSIMPORTER) |  | ||||||
|         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".") |  | ||||||
|     ENDIF(BUILD_ESSIMPORTER) |  | ||||||
|     IF(BUILD_OPENCS) |  | ||||||
|         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".") |  | ||||||
|         INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".") |  | ||||||
|     ENDIF(BUILD_OPENCS) |  | ||||||
|     IF(BUILD_WIZARD) |  | ||||||
|         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".") |  | ||||||
|     ENDIF(BUILD_WIZARD) |  | ||||||
|     if(BUILD_MYGUI_PLUGIN) |     if(BUILD_MYGUI_PLUGIN) | ||||||
|         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION ".") |         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Debug/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Debug) | ||||||
|  |         INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) | ||||||
|     ENDIF(BUILD_MYGUI_PLUGIN) |     ENDIF(BUILD_MYGUI_PLUGIN) | ||||||
| 
 | 
 | ||||||
|     IF(DESIRED_QT_VERSION MATCHES 5) |     IF(DESIRED_QT_VERSION MATCHES 5) | ||||||
|         INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION ".") |         INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug) | ||||||
|  |         INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) | ||||||
|     ENDIF() |     ENDIF() | ||||||
| 
 | 
 | ||||||
|     INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") |     INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") | ||||||
|     FILE(GLOB plugin_dir "${OpenMW_BINARY_DIR}/Release/osgPlugins-*") |     FILE(GLOB plugin_dir_debug "${OpenMW_BINARY_DIR}/Debug/osgPlugins-*") | ||||||
|     INSTALL(DIRECTORY ${plugin_dir} DESTINATION ".") |     FILE(GLOB plugin_dir_release "${OpenMW_BINARY_DIR}/Release/osgPlugins-*") | ||||||
|  |     INSTALL(DIRECTORY ${plugin_dir_debug} DESTINATION "." CONFIGURATIONS Debug) | ||||||
|  |     INSTALL(DIRECTORY ${plugin_dir_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) | ||||||
| 
 | 
 | ||||||
|     SET(CPACK_GENERATOR "NSIS") |     SET(CPACK_GENERATOR "NSIS") | ||||||
|     SET(CPACK_PACKAGE_NAME "OpenMW") |     SET(CPACK_PACKAGE_NAME "OpenMW") | ||||||
|  | @ -725,14 +723,7 @@ if (APPLE) | ||||||
|       configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) |       configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) | ||||||
|     endif () |     endif () | ||||||
| 
 | 
 | ||||||
|     set(INSTALL_SUBDIR OpenMW) |     install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime) | ||||||
| 
 |  | ||||||
|     install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) |  | ||||||
|     install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) |  | ||||||
|     install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) |  | ||||||
|     install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) |  | ||||||
|     install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) |  | ||||||
|     install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) |  | ||||||
| 
 | 
 | ||||||
|     set(CPACK_GENERATOR "DragNDrop") |     set(CPACK_GENERATOR "DragNDrop") | ||||||
|     set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) |     set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) | ||||||
|  | @ -740,8 +731,8 @@ if (APPLE) | ||||||
|     set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) |     set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) | ||||||
|     set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) |     set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) | ||||||
| 
 | 
 | ||||||
|     set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") |     set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_NAME}") | ||||||
|     set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") |     set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${OPENCS_BUNDLE_NAME}") | ||||||
| 
 | 
 | ||||||
|     install(CODE " |     install(CODE " | ||||||
|         set(BU_CHMOD_BUNDLE_ITEMS ON) |         set(BU_CHMOD_BUNDLE_ITEMS ON) | ||||||
|  | @ -785,8 +776,8 @@ if (APPLE) | ||||||
|         set(${plugins_var} ${PLUGINS} PARENT_SCOPE) |         set(${plugins_var} ${PLUGINS} PARENT_SCOPE) | ||||||
|     endfunction (install_plugins_for_bundle) |     endfunction (install_plugins_for_bundle) | ||||||
| 
 | 
 | ||||||
|     install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS) |     install_plugins_for_bundle("${APP_BUNDLE_NAME}" PLUGINS) | ||||||
|     install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) |     install_plugins_for_bundle("${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) | ||||||
| 
 | 
 | ||||||
|     set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") |     set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") | ||||||
|     set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") |     set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") | ||||||
|  | @ -828,3 +819,4 @@ if (DOXYGEN_FOUND) | ||||||
|         WORKING_DIRECTORY ${OpenMW_BINARY_DIR} |         WORKING_DIRECTORY ${OpenMW_BINARY_DIR} | ||||||
|         COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) |         COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM) | ||||||
| endif () | endif () | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -42,3 +42,7 @@ if (BUILD_WITH_CODE_COVERAGE) | ||||||
|   add_definitions (--coverage) |   add_definitions (--coverage) | ||||||
|   target_link_libraries(openmw-essimporter gcov) |   target_link_libraries(openmw-essimporter gcov) | ||||||
| endif() | endif() | ||||||
|  | 
 | ||||||
|  | if (WIN32) | ||||||
|  |   INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".") | ||||||
|  | endif(WIN32) | ||||||
|  |  | ||||||
|  | @ -271,23 +271,34 @@ private: | ||||||
| class ConvertPCDT : public Converter | class ConvertPCDT : public Converter | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     ConvertPCDT() : mFirstPersonCam(true) {} |     ConvertPCDT() | ||||||
|  |         : mFirstPersonCam(true), | ||||||
|  |           mTeleportingEnabled(true), | ||||||
|  |           mLevitationEnabled(true) | ||||||
|  |     {} | ||||||
| 
 | 
 | ||||||
|     virtual void read(ESM::ESMReader &esm) |     virtual void read(ESM::ESMReader &esm) | ||||||
|     { |     { | ||||||
|         PCDT pcdt; |         PCDT pcdt; | ||||||
|         pcdt.load(esm); |         pcdt.load(esm); | ||||||
| 
 | 
 | ||||||
|         convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam); |         convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState); | ||||||
|     } |     } | ||||||
|     virtual void write(ESM::ESMWriter &esm) |     virtual void write(ESM::ESMWriter &esm) | ||||||
|     { |     { | ||||||
|  |         esm.startRecord(ESM::REC_ENAB); | ||||||
|  |         esm.writeHNT("TELE", mTeleportingEnabled); | ||||||
|  |         esm.writeHNT("LEVT", mLevitationEnabled); | ||||||
|  |         esm.endRecord(ESM::REC_ENAB); | ||||||
|  | 
 | ||||||
|         esm.startRecord(ESM::REC_CAM_); |         esm.startRecord(ESM::REC_CAM_); | ||||||
|         esm.writeHNT("FIRS", mFirstPersonCam); |         esm.writeHNT("FIRS", mFirstPersonCam); | ||||||
|         esm.endRecord(ESM::REC_CAM_); |         esm.endRecord(ESM::REC_CAM_); | ||||||
|     } |     } | ||||||
| private: | private: | ||||||
|     bool mFirstPersonCam; |     bool mFirstPersonCam; | ||||||
|  |     bool mTeleportingEnabled; | ||||||
|  |     bool mLevitationEnabled; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class ConvertCNTC : public Converter | class ConvertCNTC : public Converter | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| namespace ESSImport | namespace ESSImport | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam) |     void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls) | ||||||
|     { |     { | ||||||
|         out.mBirthsign = pcdt.mBirthsign; |         out.mBirthsign = pcdt.mBirthsign; | ||||||
|         out.mObject.mNpcStats.mBounty = pcdt.mBounty; |         out.mObject.mNpcStats.mBounty = pcdt.mBounty; | ||||||
|  | @ -25,18 +25,28 @@ namespace ESSImport | ||||||
|             out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i]; |             out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i]; | ||||||
|         out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress; |         out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress; | ||||||
| 
 | 
 | ||||||
|         if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon) |         if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawn) | ||||||
|             out.mObject.mCreatureStats.mDrawState = 1; |             out.mObject.mCreatureStats.mDrawState = 1; | ||||||
|         if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell) |         if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawn) | ||||||
|             out.mObject.mCreatureStats.mDrawState = 2; |             out.mObject.mCreatureStats.mDrawState = 2; | ||||||
| 
 | 
 | ||||||
|         firstPersonCam = !(pcdt.mPNAM.mCameraFlags & PCDT::CameraFlag_ThirdPerson); |         firstPersonCam = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ThirdPerson); | ||||||
|  |         teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled); | ||||||
|  |         levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled); | ||||||
| 
 | 
 | ||||||
|         for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin(); |         for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin(); | ||||||
|              it != pcdt.mKnownDialogueTopics.end(); ++it) |              it != pcdt.mKnownDialogueTopics.end(); ++it) | ||||||
|         { |         { | ||||||
|             outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); |             outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled; | ||||||
|  |         controls.mControlsDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ControlsDisabled; | ||||||
|  |         controls.mJumpingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_JumpingDisabled; | ||||||
|  |         controls.mLookingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LookingDisabled; | ||||||
|  |         controls.mVanityModeDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_VanityModeDisabled; | ||||||
|  |         controls.mWeaponDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawingDisabled; | ||||||
|  |         controls.mSpellDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawingDisabled; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,11 +4,12 @@ | ||||||
| #include "importplayer.hpp" | #include "importplayer.hpp" | ||||||
| 
 | 
 | ||||||
| #include <components/esm/player.hpp> | #include <components/esm/player.hpp> | ||||||
|  | #include <components/esm/controlsstate.hpp> | ||||||
| 
 | 
 | ||||||
| namespace ESSImport | namespace ESSImport | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam); |     void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -51,6 +51,7 @@ namespace | ||||||
|         { |         { | ||||||
|             for (int x=0; x<128; ++x) |             for (int x=0; x<128; ++x) | ||||||
|             { |             { | ||||||
|  |                 assert(image->data(x,y)); | ||||||
|                 *(image->data(x,y)+2) = *it++; |                 *(image->data(x,y)+2) = *it++; | ||||||
|                 *(image->data(x,y)+1) = *it++; |                 *(image->data(x,y)+1) = *it++; | ||||||
|                 *image->data(x,y) = *it++; |                 *image->data(x,y) = *it++; | ||||||
|  | @ -422,6 +423,10 @@ namespace ESSImport | ||||||
|         writer.startRecord (ESM::REC_DIAS); |         writer.startRecord (ESM::REC_DIAS); | ||||||
|         context.mDialogueState.save(writer); |         context.mDialogueState.save(writer); | ||||||
|         writer.endRecord(ESM::REC_DIAS); |         writer.endRecord(ESM::REC_DIAS); | ||||||
|  | 
 | ||||||
|  |         writer.startRecord(ESM::REC_INPU); | ||||||
|  |         context.mControlsState.save(writer); | ||||||
|  |         writer.endRecord(ESM::REC_INPU); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| #include <components/esm/globalmap.hpp> | #include <components/esm/globalmap.hpp> | ||||||
| #include <components/esm/loadcrea.hpp> | #include <components/esm/loadcrea.hpp> | ||||||
| #include <components/esm/loadnpc.hpp> | #include <components/esm/loadnpc.hpp> | ||||||
|  | #include <components/esm/controlsstate.hpp> | ||||||
| 
 | 
 | ||||||
| #include "importnpcc.hpp" | #include "importnpcc.hpp" | ||||||
| #include "importcrec.hpp" | #include "importcrec.hpp" | ||||||
|  | @ -32,6 +33,8 @@ namespace ESSImport | ||||||
| 
 | 
 | ||||||
|         ESM::DialogueState mDialogueState; |         ESM::DialogueState mDialogueState; | ||||||
| 
 | 
 | ||||||
|  |         ESM::ControlsState mControlsState; | ||||||
|  | 
 | ||||||
|         // cells which should show an explored overlay on the global map
 |         // cells which should show an explored overlay on the global map
 | ||||||
|         std::set<std::pair<int, int> > mExploredCells; |         std::set<std::pair<int, int> > mExploredCells; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,14 +38,20 @@ struct PCDT | ||||||
| 
 | 
 | ||||||
|     std::vector<std::string> mKnownDialogueTopics; |     std::vector<std::string> mKnownDialogueTopics; | ||||||
| 
 | 
 | ||||||
|     enum DrawState_ |     enum PlayerFlags | ||||||
|     { |     { | ||||||
|         DrawState_Weapon = 0x80, |         PlayerFlags_ViewSwitchDisabled = 0x1, | ||||||
|         DrawState_Spell = 0x100 |         PlayerFlags_ControlsDisabled = 0x4, | ||||||
|     }; |         PlayerFlags_WeaponDrawn = 0x80, | ||||||
|     enum CameraFlags |         PlayerFlags_SpellDrawn = 0x100, | ||||||
|     { |         PlayerFlags_JumpingDisabled = 0x1000, | ||||||
|         CameraFlag_ThirdPerson = 0x2 |         PlayerFlags_LookingDisabled = 0x2000, | ||||||
|  |         PlayerFlags_VanityModeDisabled = 0x4000, | ||||||
|  |         PlayerFlags_WeaponDrawingDisabled = 0x8000, | ||||||
|  |         PlayerFlags_SpellDrawingDisabled = 0x10000, | ||||||
|  |         PlayerFlags_ThirdPerson = 0x20000, | ||||||
|  |         PlayerFlags_TeleportingDisabled = 0x40000, | ||||||
|  |         PlayerFlags_LevitationDisabled = 0x80000 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| #pragma pack(push) | #pragma pack(push) | ||||||
|  | @ -62,8 +68,7 @@ struct PCDT | ||||||
| 
 | 
 | ||||||
|     struct PNAM |     struct PNAM | ||||||
|     { |     { | ||||||
|         short mDrawState; // DrawState
 |         int mPlayerFlags; // controls, camera and draw state
 | ||||||
|         short mCameraFlags; // CameraFlags
 |  | ||||||
|         unsigned int mLevelProgress; |         unsigned int mLevelProgress; | ||||||
|         float mSkillProgress[27]; // skill progress, non-uniform scaled
 |         float mSkillProgress[27]; // skill progress, non-uniform scaled
 | ||||||
|         unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
 |         unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ namespace ESM | ||||||
| namespace ESSImport | namespace ESSImport | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     /// Local variable assigments for a running script
 |     /// Local variable assignments for a running script
 | ||||||
|     struct SCRI |     struct SCRI | ||||||
|     { |     { | ||||||
|         std::string mScript; |         std::string mScript; | ||||||
|  |  | ||||||
|  | @ -87,6 +87,10 @@ add_executable(openmw-launcher | ||||||
|     ${UI_HDRS} |     ${UI_HDRS} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | if (WIN32) | ||||||
|  |     INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".") | ||||||
|  | endif (WIN32) | ||||||
|  | 
 | ||||||
| target_link_libraries(openmw-launcher | target_link_libraries(openmw-launcher | ||||||
|     ${SDL2_LIBRARY_ONLY} |     ${SDL2_LIBRARY_ONLY} | ||||||
|     components |     components | ||||||
|  |  | ||||||
|  | @ -35,14 +35,6 @@ int main(int argc, char *argv[]) | ||||||
|         // Now we make sure the current dir is set to application path
 |         // Now we make sure the current dir is set to application path
 | ||||||
|         QDir dir(QCoreApplication::applicationDirPath()); |         QDir dir(QCoreApplication::applicationDirPath()); | ||||||
| 
 | 
 | ||||||
|         #ifdef Q_OS_MAC |  | ||||||
|         if (dir.dirName() == "MacOS") { |  | ||||||
|             dir.cdUp(); |  | ||||||
|             dir.cdUp(); |  | ||||||
|             dir.cdUp(); |  | ||||||
|         } |  | ||||||
|         #endif |  | ||||||
| 
 |  | ||||||
|         QDir::setCurrent(dir.absolutePath()); |         QDir::setCurrent(dir.absolutePath()); | ||||||
| 
 | 
 | ||||||
|         Launcher::MainDialog mainWin; |         Launcher::MainDialog mainWin; | ||||||
|  |  | ||||||
|  | @ -293,6 +293,7 @@ bool Launcher::MainDialog::setupGameSettings() | ||||||
| { | { | ||||||
|     mGameSettings.clear(); |     mGameSettings.clear(); | ||||||
| 
 | 
 | ||||||
|  |     QString localPath = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str()); | ||||||
|     QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); |     QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()); | ||||||
|     QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); |     QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str()); | ||||||
| 
 | 
 | ||||||
|  | @ -320,13 +321,13 @@ bool Launcher::MainDialog::setupGameSettings() | ||||||
|     // Now the rest - priority: user > local > global
 |     // Now the rest - priority: user > local > global
 | ||||||
|     QStringList paths; |     QStringList paths; | ||||||
|     paths.append(globalPath + QString("openmw.cfg")); |     paths.append(globalPath + QString("openmw.cfg")); | ||||||
|     paths.append(QString("openmw.cfg")); |     paths.append(localPath + QString("openmw.cfg")); | ||||||
|     paths.append(userPath + QString("openmw.cfg")); |     paths.append(userPath + QString("openmw.cfg")); | ||||||
| 
 | 
 | ||||||
|     foreach (const QString &path, paths) { |     foreach (const QString &path2, paths) { | ||||||
|         qDebug() << "Loading config file:" << path.toUtf8().constData(); |         qDebug() << "Loading config file:" << path2.toUtf8().constData(); | ||||||
| 
 | 
 | ||||||
|         QFile file(path); |         file.setFileName(path2); | ||||||
|         if (file.exists()) { |         if (file.exists()) { | ||||||
|             if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { |             if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | ||||||
|                 cfgError(tr("Error opening OpenMW configuration file"), |                 cfgError(tr("Error opening OpenMW configuration file"), | ||||||
|  | @ -346,13 +347,13 @@ bool Launcher::MainDialog::setupGameSettings() | ||||||
|     QStringList dataDirs; |     QStringList dataDirs; | ||||||
| 
 | 
 | ||||||
|     // Check if the paths actually contain data files
 |     // Check if the paths actually contain data files
 | ||||||
|     foreach (const QString path, mGameSettings.getDataDirs()) { |     foreach (const QString path3, mGameSettings.getDataDirs()) { | ||||||
|         QDir dir(path); |         QDir dir(path3); | ||||||
|         QStringList filters; |         QStringList filters; | ||||||
|         filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; |         filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; | ||||||
| 
 | 
 | ||||||
|         if (!dir.entryList(filters).isEmpty()) |         if (!dir.entryList(filters).isEmpty()) | ||||||
|             dataDirs.append(path); |             dataDirs.append(path3); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (dataDirs.isEmpty()) |     if (dataDirs.isEmpty()) | ||||||
|  |  | ||||||
|  | @ -22,7 +22,8 @@ target_link_libraries(openmw-iniimporter | ||||||
| if (WIN32) | if (WIN32) | ||||||
|     target_link_libraries(openmw-iniimporter |     target_link_libraries(openmw-iniimporter | ||||||
|     ${Boost_LOCALE_LIBRARY}) |     ${Boost_LOCALE_LIBRARY}) | ||||||
| endif() |     INSTALL(TARGETS openmw-iniimporter RUNTIME DESTINATION ".") | ||||||
|  | endif(WIN32) | ||||||
| 
 | 
 | ||||||
| if (MINGW) | if (MINGW) | ||||||
|   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") |   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") | ||||||
|  |  | ||||||
|  | @ -162,9 +162,15 @@ endif() | ||||||
| include_directories(${CMAKE_CURRENT_BINARY_DIR}) | include_directories(${CMAKE_CURRENT_BINARY_DIR}) | ||||||
| 
 | 
 | ||||||
| if(APPLE) | if(APPLE) | ||||||
|     set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns) |     set (OPENCS_MAC_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns") | ||||||
|  |     set (OPENCS_CFG "${OpenMW_BINARY_DIR}/openmw-cs.cfg") | ||||||
|  |     set (OPENCS_DEFAULT_FILTERS_FILE "${OpenMW_BINARY_DIR}/resources/defaultfilters") | ||||||
|  |     set (OPENCS_OPENMW_CFG "${OpenMW_BINARY_DIR}/openmw.cfg") | ||||||
| else() | else() | ||||||
|     set (OPENCS_MAC_ICON "") |     set (OPENCS_MAC_ICON "") | ||||||
|  |     set (OPENCS_CFG "") | ||||||
|  |     set (OPENCS_DEFAULT_FILTERS_FILE "") | ||||||
|  |     set (OPENCS_OPENMW_CFG "") | ||||||
| endif(APPLE) | endif(APPLE) | ||||||
| 
 | 
 | ||||||
| add_executable(openmw-cs | add_executable(openmw-cs | ||||||
|  | @ -174,12 +180,23 @@ add_executable(openmw-cs | ||||||
|     ${OPENCS_MOC_SRC} |     ${OPENCS_MOC_SRC} | ||||||
|     ${OPENCS_RES_SRC} |     ${OPENCS_RES_SRC} | ||||||
|     ${OPENCS_MAC_ICON} |     ${OPENCS_MAC_ICON} | ||||||
|  |     ${OPENCS_CFG} | ||||||
|  |     ${OPENCS_DEFAULT_FILTERS_FILE} | ||||||
|  |     ${OPENCS_OPENMW_CFG} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| if(APPLE) | if(APPLE) | ||||||
|  |     set(OPENCS_BUNDLE_NAME "OpenMW-CS") | ||||||
|  |     set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources") | ||||||
|  | 
 | ||||||
|  |     set(OPENMW_MYGUI_FILES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR}) | ||||||
|  |     set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR}) | ||||||
|  | 
 | ||||||
|  |     add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files) | ||||||
|  | 
 | ||||||
|     set_target_properties(openmw-cs PROPERTIES |     set_target_properties(openmw-cs PROPERTIES | ||||||
|         RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" |         RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" | ||||||
|         OUTPUT_NAME "OpenMW-CS" |         OUTPUT_NAME ${OPENCS_BUNDLE_NAME} | ||||||
|         MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns" |         MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns" | ||||||
|         MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" |         MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" | ||||||
|         MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" |         MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" | ||||||
|  | @ -190,6 +207,16 @@ if(APPLE) | ||||||
| 
 | 
 | ||||||
|     set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES |     set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES | ||||||
|         MACOSX_PACKAGE_LOCATION Resources) |         MACOSX_PACKAGE_LOCATION Resources) | ||||||
|  |     set_source_files_properties(${OPENCS_CFG} PROPERTIES | ||||||
|  |         MACOSX_PACKAGE_LOCATION Resources) | ||||||
|  |     set_source_files_properties(${OPENCS_DEFAULT_FILTERS_FILE} PROPERTIES | ||||||
|  |         MACOSX_PACKAGE_LOCATION Resources/resources) | ||||||
|  |     set_source_files_properties(${OPENCS_OPENMW_CFG} PROPERTIES | ||||||
|  |         MACOSX_PACKAGE_LOCATION Resources) | ||||||
|  | 
 | ||||||
|  |     add_custom_command(TARGET openmw-cs | ||||||
|  |         POST_BUILD | ||||||
|  |         COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${OPENCS_BUNDLE_RESOURCES_DIR}/resources") | ||||||
| endif(APPLE) | endif(APPLE) | ||||||
| 
 | 
 | ||||||
| target_link_libraries(openmw-cs | target_link_libraries(openmw-cs | ||||||
|  | @ -223,9 +250,18 @@ endif() | ||||||
| 
 | 
 | ||||||
| if (WIN32) | if (WIN32) | ||||||
|     target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) |     target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) | ||||||
|  |     INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".") | ||||||
|  |     INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".") | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
|  | if (MSVC) | ||||||
|  |     # Debug version needs increased number of sections beyond 2^16 | ||||||
|  |     if (CMAKE_CL_64) | ||||||
|  |         set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj") | ||||||
|  |     endif (CMAKE_CL_64) | ||||||
|  | endif (MSVC) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| if(APPLE) | if(APPLE) | ||||||
|     INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) |     INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | @ -62,11 +62,6 @@ int main(int argc, char *argv[]) | ||||||
| 
 | 
 | ||||||
|     #ifdef Q_OS_MAC |     #ifdef Q_OS_MAC | ||||||
|         QDir dir(QCoreApplication::applicationDirPath()); |         QDir dir(QCoreApplication::applicationDirPath()); | ||||||
|         if (dir.dirName() == "MacOS") { |  | ||||||
|             dir.cdUp(); |  | ||||||
|             dir.cdUp(); |  | ||||||
|             dir.cdUp(); |  | ||||||
|         } |  | ||||||
|         QDir::setCurrent(dir.absolutePath()); |         QDir::setCurrent(dir.absolutePath()); | ||||||
|     #endif |     #endif | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ namespace CSMPrefs | ||||||
|             enum SecondaryMode |             enum SecondaryMode | ||||||
|             { |             { | ||||||
|                 SM_Replace, ///< The secondary signal replaces the regular signal when the modifier is active
 |                 SM_Replace, ///< The secondary signal replaces the regular signal when the modifier is active
 | ||||||
|                 SM_Detach,  ///< The secondary signal is emitted independant of the regular signal, even if not active
 |                 SM_Detach,  ///< The secondary signal is emitted independent of the regular signal, even if not active
 | ||||||
|                 SM_Ignore   ///< The secondary signal will not ever be emitted
 |                 SM_Ignore   ///< The secondary signal will not ever be emitted
 | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -184,7 +184,7 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes | ||||||
|             CSMWorld::LandTexture texture = |             CSMWorld::LandTexture texture = | ||||||
|                 mState.mSource.getData().getLandTextures().getRecord (index).get(); |                 mState.mSource.getData().getLandTextures().getRecord (index).get(); | ||||||
| 
 | 
 | ||||||
|             std::ostringstream stream; |             stream.clear(); | ||||||
|             stream << mNext->second-1 << "_0"; |             stream << mNext->second-1 << "_0"; | ||||||
| 
 | 
 | ||||||
|             texture.mIndex = mNext->second-1; |             texture.mIndex = mNext->second-1; | ||||||
|  |  | ||||||
|  | @ -659,7 +659,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( | ||||||
|     { |     { | ||||||
|         if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
 |         if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
 | ||||||
|         { |         { | ||||||
|             messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happend?
 |             messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen?
 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -915,7 +915,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( | ||||||
|                 id + " contains non-existing item (" + itemName + ")")); |                 id + " contains non-existing item (" + itemName + ")")); | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             // Needs to accomodate Containers, Creatures, and NPCs
 |             // Needs to accommodate containers, creatures, and NPCs
 | ||||||
|             switch (localIndex.second) |             switch (localIndex.second) | ||||||
|             { |             { | ||||||
|             case CSMWorld::UniversalId::Type_Potion: |             case CSMWorld::UniversalId::Type_Potion: | ||||||
|  |  | ||||||
|  | @ -1627,264 +1627,264 @@ const char * CSMWorld::DefaultGmsts::OptionalStrings[CSMWorld::DefaultGmsts::Opt | ||||||
| 
 | 
 | ||||||
| const float CSMWorld::DefaultGmsts::FloatsDefaultValues[CSMWorld::DefaultGmsts::FloatCount] = | const float CSMWorld::DefaultGmsts::FloatsDefaultValues[CSMWorld::DefaultGmsts::FloatCount] = | ||||||
| { | { | ||||||
|     0.3,    // fAIFleeFleeMult
 |     0.3f,    // fAIFleeFleeMult
 | ||||||
|     7.0,    // fAIFleeHealthMult
 |     7.0f,    // fAIFleeHealthMult
 | ||||||
|     3.0,    // fAIMagicSpellMult
 |     3.0f,    // fAIMagicSpellMult
 | ||||||
|     1.0,    // fAIMeleeArmorMult
 |     1.0f,    // fAIMeleeArmorMult
 | ||||||
|     1.0,    // fAIMeleeSummWeaponMult
 |     1.0f,    // fAIMeleeSummWeaponMult
 | ||||||
|     2.0,    // fAIMeleeWeaponMult
 |     2.0f,    // fAIMeleeWeaponMult
 | ||||||
|     5.0,    // fAIRangeMagicSpellMult
 |     5.0f,    // fAIRangeMagicSpellMult
 | ||||||
|     5.0,    // fAIRangeMeleeWeaponMult
 |     5.0f,    // fAIRangeMeleeWeaponMult
 | ||||||
|     2000.0, // fAlarmRadius
 |     2000.0f, // fAlarmRadius
 | ||||||
|     1.0,    // fAthleticsRunBonus
 |     1.0f,    // fAthleticsRunBonus
 | ||||||
|     40.0,   // fAudioDefaultMaxDistance
 |     40.0f,   // fAudioDefaultMaxDistance
 | ||||||
|     5.0,    // fAudioDefaultMinDistance
 |     5.0f,    // fAudioDefaultMinDistance
 | ||||||
|     50.0,   // fAudioMaxDistanceMult
 |     50.0f,   // fAudioMaxDistanceMult
 | ||||||
|     20.0,   // fAudioMinDistanceMult
 |     20.0f,   // fAudioMinDistanceMult
 | ||||||
|     60.0,   // fAudioVoiceDefaultMaxDistance
 |     60.0f,   // fAudioVoiceDefaultMaxDistance
 | ||||||
|     10.0,   // fAudioVoiceDefaultMinDistance
 |     10.0f,   // fAudioVoiceDefaultMinDistance
 | ||||||
|     50.0,   // fAutoPCSpellChance
 |     50.0f,   // fAutoPCSpellChance
 | ||||||
|     80.0,   // fAutoSpellChance
 |     80.0f,   // fAutoSpellChance
 | ||||||
|     50.0,   // fBargainOfferBase
 |     50.0f,   // fBargainOfferBase
 | ||||||
|     -4.0,   // fBargainOfferMulti
 |     -4.0f,   // fBargainOfferMulti
 | ||||||
|     24.0,   // fBarterGoldResetDelay
 |     24.0f,   // fBarterGoldResetDelay
 | ||||||
|     1.75,   // fBaseRunMultiplier
 |     1.75f,   // fBaseRunMultiplier
 | ||||||
|     1.25,   // fBlockStillBonus
 |     1.25f,   // fBlockStillBonus
 | ||||||
|     150.0,  // fBribe1000Mod
 |     150.0f,  // fBribe1000Mod
 | ||||||
|     75.0,   // fBribe100Mod
 |     75.0f,   // fBribe100Mod
 | ||||||
|     35.0,   // fBribe10Mod
 |     35.0f,   // fBribe10Mod
 | ||||||
|     60.0,   // fCombatAngleXY
 |     60.0f,   // fCombatAngleXY
 | ||||||
|     60.0,   // fCombatAngleZ
 |     60.0f,   // fCombatAngleZ
 | ||||||
|     0.25,   // fCombatArmorMinMult
 |     0.25f,   // fCombatArmorMinMult
 | ||||||
|     -90.0,  // fCombatBlockLeftAngle
 |     -90.0f,  // fCombatBlockLeftAngle
 | ||||||
|     30.0,   // fCombatBlockRightAngle
 |     30.0f,   // fCombatBlockRightAngle
 | ||||||
|     4.0,    // fCombatCriticalStrikeMult
 |     4.0f,    // fCombatCriticalStrikeMult
 | ||||||
|     0.1,    // fCombatDelayCreature
 |     0.1f,    // fCombatDelayCreature
 | ||||||
|     0.1,    // fCombatDelayNPC
 |     0.1f,    // fCombatDelayNPC
 | ||||||
|     128.0,  // fCombatDistance
 |     128.0f,  // fCombatDistance
 | ||||||
|     0.3,    // fCombatDistanceWerewolfMod
 |     0.3f,    // fCombatDistanceWerewolfMod
 | ||||||
|     30.0,   // fCombatForceSideAngle
 |     30.0f,   // fCombatForceSideAngle
 | ||||||
|     0.2,    // fCombatInvisoMult
 |     0.2f,    // fCombatInvisoMult
 | ||||||
|     1.5,    // fCombatKODamageMult
 |     1.5f,    // fCombatKODamageMult
 | ||||||
|     45.0,   // fCombatTorsoSideAngle
 |     45.0f,   // fCombatTorsoSideAngle
 | ||||||
|     0.3,    // fCombatTorsoStartPercent
 |     0.3f,    // fCombatTorsoStartPercent
 | ||||||
|     0.8,    // fCombatTorsoStopPercent
 |     0.8f,    // fCombatTorsoStopPercent
 | ||||||
|     15.0,   // fConstantEffectMult
 |     15.0f,   // fConstantEffectMult
 | ||||||
|     72.0,   // fCorpseClearDelay
 |     72.0f,   // fCorpseClearDelay
 | ||||||
|     72.0,   // fCorpseRespawnDelay
 |     72.0f,   // fCorpseRespawnDelay
 | ||||||
|     0.5,    // fCrimeGoldDiscountMult
 |     0.5f,    // fCrimeGoldDiscountMult
 | ||||||
|     0.9,    // fCrimeGoldTurnInMult
 |     0.9f,    // fCrimeGoldTurnInMult
 | ||||||
|     1.0,    // fCrimeStealing
 |     1.0f,    // fCrimeStealing
 | ||||||
|     0.5,    // fDamageStrengthBase
 |     0.5f,    // fDamageStrengthBase
 | ||||||
|     0.1,    // fDamageStrengthMult
 |     0.1f,    // fDamageStrengthMult
 | ||||||
|     5.0,    // fDifficultyMult
 |     5.0f,    // fDifficultyMult
 | ||||||
|     2.5,    // fDiseaseXferChance
 |     2.5f,    // fDiseaseXferChance
 | ||||||
|     -10.0,  // fDispAttacking
 |     -10.0f,  // fDispAttacking
 | ||||||
|     -1.0,   // fDispBargainFailMod
 |     -1.0f,   // fDispBargainFailMod
 | ||||||
|     1.0,    // fDispBargainSuccessMod
 |     1.0f,    // fDispBargainSuccessMod
 | ||||||
|     0.0,    // fDispCrimeMod
 |     0.0f,    // fDispCrimeMod
 | ||||||
|     -10.0,  // fDispDiseaseMod
 |     -10.0f,  // fDispDiseaseMod
 | ||||||
|     3.0,    // fDispFactionMod
 |     3.0f,    // fDispFactionMod
 | ||||||
|     1.0,    // fDispFactionRankBase
 |     1.0f,    // fDispFactionRankBase
 | ||||||
|     0.5,    // fDispFactionRankMult
 |     0.5f,    // fDispFactionRankMult
 | ||||||
|     1.0,    // fDispositionMod
 |     1.0f,    // fDispositionMod
 | ||||||
|     50.0,   // fDispPersonalityBase
 |     50.0f,   // fDispPersonalityBase
 | ||||||
|     0.5,    // fDispPersonalityMult
 |     0.5f,    // fDispPersonalityMult
 | ||||||
|     -25.0,  // fDispPickPocketMod
 |     -25.0f,  // fDispPickPocketMod
 | ||||||
|     5.0,    // fDispRaceMod
 |     5.0f,    // fDispRaceMod
 | ||||||
|     -0.5,   // fDispStealing
 |     -0.5f,   // fDispStealing
 | ||||||
|     -5.0,   // fDispWeaponDrawn
 |     -5.0f,   // fDispWeaponDrawn
 | ||||||
|     0.5,    // fEffectCostMult
 |     0.5f,    // fEffectCostMult
 | ||||||
|     0.1,    // fElementalShieldMult
 |     0.1f,    // fElementalShieldMult
 | ||||||
|     3.0,    // fEnchantmentChanceMult
 |     3.0f,    // fEnchantmentChanceMult
 | ||||||
|     0.5,    // fEnchantmentConstantChanceMult
 |     0.5f,    // fEnchantmentConstantChanceMult
 | ||||||
|     100.0,  // fEnchantmentConstantDurationMult
 |     100.0f,  // fEnchantmentConstantDurationMult
 | ||||||
|     0.1,    // fEnchantmentMult
 |     0.1f,    // fEnchantmentMult
 | ||||||
|     1000.0, // fEnchantmentValueMult
 |     1000.0f, // fEnchantmentValueMult
 | ||||||
|     0.3,    // fEncumberedMoveEffect
 |     0.3f,    // fEncumberedMoveEffect
 | ||||||
|     5.0,    // fEncumbranceStrMult
 |     5.0f,    // fEncumbranceStrMult
 | ||||||
|     0.04,   // fEndFatigueMult
 |     0.04f,   // fEndFatigueMult
 | ||||||
|     0.25,   // fFallAcroBase
 |     0.25f,   // fFallAcroBase
 | ||||||
|     0.01,   // fFallAcroMult
 |     0.01f,   // fFallAcroMult
 | ||||||
|     400.0,  // fFallDamageDistanceMin
 |     400.0f,  // fFallDamageDistanceMin
 | ||||||
|     0.0,    // fFallDistanceBase
 |     0.0f,    // fFallDistanceBase
 | ||||||
|     0.07,   // fFallDistanceMult
 |     0.07f,   // fFallDistanceMult
 | ||||||
|     2.0,    // fFatigueAttackBase
 |     2.0f,    // fFatigueAttackBase
 | ||||||
|     0.0,    // fFatigueAttackMult
 |     0.0f,    // fFatigueAttackMult
 | ||||||
|     1.25,   // fFatigueBase
 |     1.25f,   // fFatigueBase
 | ||||||
|     4.0,    // fFatigueBlockBase
 |     4.0f,    // fFatigueBlockBase
 | ||||||
|     0.0,    // fFatigueBlockMult
 |     0.0f,    // fFatigueBlockMult
 | ||||||
|     5.0,    // fFatigueJumpBase
 |     5.0f,    // fFatigueJumpBase
 | ||||||
|     0.0,    // fFatigueJumpMult
 |     0.0f,    // fFatigueJumpMult
 | ||||||
|     0.5,    // fFatigueMult
 |     0.5f,    // fFatigueMult
 | ||||||
|     2.5,    // fFatigueReturnBase
 |     2.5f,    // fFatigueReturnBase
 | ||||||
|     0.02,   // fFatigueReturnMult
 |     0.02f,   // fFatigueReturnMult
 | ||||||
|     5.0,    // fFatigueRunBase
 |     5.0f,    // fFatigueRunBase
 | ||||||
|     2.0,    // fFatigueRunMult
 |     2.0f,    // fFatigueRunMult
 | ||||||
|     1.5,    // fFatigueSneakBase
 |     1.5f,    // fFatigueSneakBase
 | ||||||
|     1.5,    // fFatigueSneakMult
 |     1.5f,    // fFatigueSneakMult
 | ||||||
|     0.0,    // fFatigueSpellBase
 |     0.0f,    // fFatigueSpellBase
 | ||||||
|     0.0,    // fFatigueSpellCostMult
 |     0.0f,    // fFatigueSpellCostMult
 | ||||||
|     0.0,    // fFatigueSpellMult
 |     0.0f,    // fFatigueSpellMult
 | ||||||
|     7.0,    // fFatigueSwimRunBase
 |     7.0f,    // fFatigueSwimRunBase
 | ||||||
|     0.0,    // fFatigueSwimRunMult
 |     0.0f,    // fFatigueSwimRunMult
 | ||||||
|     2.5,    // fFatigueSwimWalkBase
 |     2.5f,    // fFatigueSwimWalkBase
 | ||||||
|     0.0,    // fFatigueSwimWalkMult
 |     0.0f,    // fFatigueSwimWalkMult
 | ||||||
|     0.2,    // fFightDispMult
 |     0.2f,    // fFightDispMult
 | ||||||
|     0.005,  // fFightDistanceMultiplier
 |     0.005f,  // fFightDistanceMultiplier
 | ||||||
|     50.0,   // fFightStealing
 |     50.0f,   // fFightStealing
 | ||||||
|     3000.0, // fFleeDistance
 |     3000.0f, // fFleeDistance
 | ||||||
|     512.0,  // fGreetDistanceReset
 |     512.0f,  // fGreetDistanceReset
 | ||||||
|     0.1,    // fHandtoHandHealthPer
 |     0.1f,    // fHandtoHandHealthPer
 | ||||||
|     1.0,    // fHandToHandReach
 |     1.0f,    // fHandToHandReach
 | ||||||
|     0.5,    // fHoldBreathEndMult
 |     0.5f,    // fHoldBreathEndMult
 | ||||||
|     20.0,   // fHoldBreathTime
 |     20.0f,   // fHoldBreathTime
 | ||||||
|     0.75,   // fIdleChanceMultiplier
 |     0.75f,   // fIdleChanceMultiplier
 | ||||||
|     1.0,    // fIngredientMult
 |     1.0f,    // fIngredientMult
 | ||||||
|     0.5,    // fInteriorHeadTrackMult
 |     0.5f,    // fInteriorHeadTrackMult
 | ||||||
|     128.0,  // fJumpAcrobaticsBase
 |     128.0f,  // fJumpAcrobaticsBase
 | ||||||
|     4.0,    // fJumpAcroMultiplier
 |     4.0f,    // fJumpAcroMultiplier
 | ||||||
|     0.5,    // fJumpEncumbranceBase
 |     0.5f,    // fJumpEncumbranceBase
 | ||||||
|     1.0,    // fJumpEncumbranceMultiplier
 |     1.0f,    // fJumpEncumbranceMultiplier
 | ||||||
|     0.5,    // fJumpMoveBase
 |     0.5f,    // fJumpMoveBase
 | ||||||
|     0.5,    // fJumpMoveMult
 |     0.5f,    // fJumpMoveMult
 | ||||||
|     1.0,    // fJumpRunMultiplier
 |     1.0f,    // fJumpRunMultiplier
 | ||||||
|     0.5,    // fKnockDownMult
 |     0.5f,    // fKnockDownMult
 | ||||||
|     5.0,    // fLevelMod
 |     5.0f,    // fLevelMod
 | ||||||
|     0.1,    // fLevelUpHealthEndMult
 |     0.1f,    // fLevelUpHealthEndMult
 | ||||||
|     0.6,    // fLightMaxMod
 |     0.6f,    // fLightMaxMod
 | ||||||
|     10.0,   // fLuckMod
 |     10.0f,   // fLuckMod
 | ||||||
|     10.0,   // fMagesGuildTravel
 |     10.0f,   // fMagesGuildTravel
 | ||||||
|     1.5,    // fMagicCreatureCastDelay
 |     1.5f,    // fMagicCreatureCastDelay
 | ||||||
|     0.0167, // fMagicDetectRefreshRate
 |     0.0167f, // fMagicDetectRefreshRate
 | ||||||
|     1.0,    // fMagicItemConstantMult
 |     1.0f,    // fMagicItemConstantMult
 | ||||||
|     1.0,    // fMagicItemCostMult
 |     1.0f,    // fMagicItemCostMult
 | ||||||
|     1.0,    // fMagicItemOnceMult
 |     1.0f,    // fMagicItemOnceMult
 | ||||||
|     1.0,    // fMagicItemPriceMult
 |     1.0f,    // fMagicItemPriceMult
 | ||||||
|     0.05,   // fMagicItemRechargePerSecond
 |     0.05f,   // fMagicItemRechargePerSecond
 | ||||||
|     1.0,    // fMagicItemStrikeMult
 |     1.0f,    // fMagicItemStrikeMult
 | ||||||
|     1.0,    // fMagicItemUsedMult
 |     1.0f,    // fMagicItemUsedMult
 | ||||||
|     3.0,    // fMagicStartIconBlink
 |     3.0f,    // fMagicStartIconBlink
 | ||||||
|     0.5,    // fMagicSunBlockedMult
 |     0.5f,    // fMagicSunBlockedMult
 | ||||||
|     0.75,   // fMajorSkillBonus
 |     0.75f,   // fMajorSkillBonus
 | ||||||
|     300.0,  // fMaxFlySpeed
 |     300.0f,  // fMaxFlySpeed
 | ||||||
|     0.5,    // fMaxHandToHandMult
 |     0.5f,    // fMaxHandToHandMult
 | ||||||
|     400.0,  // fMaxHeadTrackDistance
 |     400.0f,  // fMaxHeadTrackDistance
 | ||||||
|     200.0,  // fMaxWalkSpeed
 |     200.0f,  // fMaxWalkSpeed
 | ||||||
|     300.0,  // fMaxWalkSpeedCreature
 |     300.0f,  // fMaxWalkSpeedCreature
 | ||||||
|     0.9,    // fMedMaxMod
 |     0.9f,    // fMedMaxMod
 | ||||||
|     0.1,    // fMessageTimePerChar
 |     0.1f,    // fMessageTimePerChar
 | ||||||
|     5.0,    // fMinFlySpeed
 |     5.0f,    // fMinFlySpeed
 | ||||||
|     0.1,    // fMinHandToHandMult
 |     0.1f,    // fMinHandToHandMult
 | ||||||
|     1.0,    // fMinorSkillBonus
 |     1.0f,    // fMinorSkillBonus
 | ||||||
|     100.0,  // fMinWalkSpeed
 |     100.0f,  // fMinWalkSpeed
 | ||||||
|     5.0,    // fMinWalkSpeedCreature
 |     5.0f,    // fMinWalkSpeedCreature
 | ||||||
|     1.25,   // fMiscSkillBonus
 |     1.25f,   // fMiscSkillBonus
 | ||||||
|     2.0,    // fNPCbaseMagickaMult
 |     2.0f,    // fNPCbaseMagickaMult
 | ||||||
|     0.5,    // fNPCHealthBarFade
 |     0.5f,    // fNPCHealthBarFade
 | ||||||
|     3.0,    // fNPCHealthBarTime
 |     3.0f,    // fNPCHealthBarTime
 | ||||||
|     1.0,    // fPCbaseMagickaMult
 |     1.0f,    // fPCbaseMagickaMult
 | ||||||
|     0.3,    // fPerDieRollMult
 |     0.3f,    // fPerDieRollMult
 | ||||||
|     5.0,    // fPersonalityMod
 |     5.0f,    // fPersonalityMod
 | ||||||
|     1.0,    // fPerTempMult
 |     1.0f,    // fPerTempMult
 | ||||||
|     -1.0,   // fPickLockMult
 |     -1.0f,   // fPickLockMult
 | ||||||
|     0.3,    // fPickPocketMod
 |     0.3f,    // fPickPocketMod
 | ||||||
|     20.0,   // fPotionMinUsefulDuration
 |     20.0f,   // fPotionMinUsefulDuration
 | ||||||
|     0.5,    // fPotionStrengthMult
 |     0.5f,    // fPotionStrengthMult
 | ||||||
|     0.5,    // fPotionT1DurMult
 |     0.5f,    // fPotionT1DurMult
 | ||||||
|     1.5,    // fPotionT1MagMult
 |     1.5f,    // fPotionT1MagMult
 | ||||||
|     20.0,   // fPotionT4BaseStrengthMult
 |     20.0f,   // fPotionT4BaseStrengthMult
 | ||||||
|     12.0,   // fPotionT4EquipStrengthMult
 |     12.0f,   // fPotionT4EquipStrengthMult
 | ||||||
|     3000.0, // fProjectileMaxSpeed
 |     3000.0f, // fProjectileMaxSpeed
 | ||||||
|     400.0,  // fProjectileMinSpeed
 |     400.0f,  // fProjectileMinSpeed
 | ||||||
|     25.0,   // fProjectileThrownStoreChance
 |     25.0f,   // fProjectileThrownStoreChance
 | ||||||
|     3.0,    // fRepairAmountMult
 |     3.0f,    // fRepairAmountMult
 | ||||||
|     1.0,    // fRepairMult
 |     1.0f,    // fRepairMult
 | ||||||
|     1.0,    // fReputationMod
 |     1.0f,    // fReputationMod
 | ||||||
|     0.15,   // fRestMagicMult
 |     0.15f,   // fRestMagicMult
 | ||||||
|     0.0,    // fSeriousWoundMult
 |     0.0f,    // fSeriousWoundMult
 | ||||||
|     0.25,   // fSleepRandMod
 |     0.25f,   // fSleepRandMod
 | ||||||
|     0.3,    // fSleepRestMod
 |     0.3f,    // fSleepRestMod
 | ||||||
|     -1.0,   // fSneakBootMult
 |     -1.0f,   // fSneakBootMult
 | ||||||
|     0.5,    // fSneakDistanceBase
 |     0.5f,    // fSneakDistanceBase
 | ||||||
|     0.002,  // fSneakDistanceMultiplier
 |     0.002f,  // fSneakDistanceMultiplier
 | ||||||
|     0.5,    // fSneakNoViewMult
 |     0.5f,    // fSneakNoViewMult
 | ||||||
|     1.0,    // fSneakSkillMult
 |     1.0f,    // fSneakSkillMult
 | ||||||
|     0.75,   // fSneakSpeedMultiplier
 |     0.75f,   // fSneakSpeedMultiplier
 | ||||||
|     1.0,    // fSneakUseDelay
 |     1.0f,    // fSneakUseDelay
 | ||||||
|     500.0,  // fSneakUseDist
 |     500.0f,  // fSneakUseDist
 | ||||||
|     1.5,    // fSneakViewMult
 |     1.5f,    // fSneakViewMult
 | ||||||
|     3.0,    // fSoulGemMult
 |     3.0f,    // fSoulGemMult
 | ||||||
|     0.8,    // fSpecialSkillBonus
 |     0.8f,    // fSpecialSkillBonus
 | ||||||
|     7.0,    // fSpellMakingValueMult
 |     7.0f,    // fSpellMakingValueMult
 | ||||||
|     2.0,    // fSpellPriceMult
 |     2.0f,    // fSpellPriceMult
 | ||||||
|     10.0,   // fSpellValueMult
 |     10.0f,   // fSpellValueMult
 | ||||||
|     0.25,   // fStromWalkMult
 |     0.25f,   // fStromWalkMult
 | ||||||
|     0.7,    // fStromWindSpeed
 |     0.7f,    // fStromWindSpeed
 | ||||||
|     3.0,    // fSuffocationDamage
 |     3.0f,    // fSuffocationDamage
 | ||||||
|     0.9,    // fSwimHeightScale
 |     0.9f,    // fSwimHeightScale
 | ||||||
|     0.1,    // fSwimRunAthleticsMult
 |     0.1f,    // fSwimRunAthleticsMult
 | ||||||
|     0.5,    // fSwimRunBase
 |     0.5f,    // fSwimRunBase
 | ||||||
|     0.02,   // fSwimWalkAthleticsMult
 |     0.02f,   // fSwimWalkAthleticsMult
 | ||||||
|     0.5,    // fSwimWalkBase
 |     0.5f,    // fSwimWalkBase
 | ||||||
|     1.0,    // fSwingBlockBase
 |     1.0f,    // fSwingBlockBase
 | ||||||
|     1.0,    // fSwingBlockMult
 |     1.0f,    // fSwingBlockMult
 | ||||||
|     1000.0, // fTargetSpellMaxSpeed
 |     1000.0f, // fTargetSpellMaxSpeed
 | ||||||
|     1000.0, // fThrownWeaponMaxSpeed
 |     1000.0f, // fThrownWeaponMaxSpeed
 | ||||||
|     300.0,  // fThrownWeaponMinSpeed
 |     300.0f,  // fThrownWeaponMinSpeed
 | ||||||
|     0.0,    // fTrapCostMult
 |     0.0f,    // fTrapCostMult
 | ||||||
|     4000.0, // fTravelMult
 |     4000.0f, // fTravelMult
 | ||||||
|     16000.0,// fTravelTimeMult
 |     16000.0f,// fTravelTimeMult
 | ||||||
|     0.1,    // fUnarmoredBase1
 |     0.1f,    // fUnarmoredBase1
 | ||||||
|     0.065,  // fUnarmoredBase2
 |     0.065f,  // fUnarmoredBase2
 | ||||||
|     30.0,   // fVanityDelay
 |     30.0f,   // fVanityDelay
 | ||||||
|     10.0,   // fVoiceIdleOdds
 |     10.0f,   // fVoiceIdleOdds
 | ||||||
|     0.0,    // fWaterReflectUpdateAlways
 |     0.0f,    // fWaterReflectUpdateAlways
 | ||||||
|     10.0,   // fWaterReflectUpdateSeldom
 |     10.0f,   // fWaterReflectUpdateSeldom
 | ||||||
|     0.1,    // fWeaponDamageMult
 |     0.1f,    // fWeaponDamageMult
 | ||||||
|     1.0,    // fWeaponFatigueBlockMult
 |     1.0f,    // fWeaponFatigueBlockMult
 | ||||||
|     0.25,   // fWeaponFatigueMult
 |     0.25f,   // fWeaponFatigueMult
 | ||||||
|     150.0,  // fWereWolfAcrobatics
 |     150.0f,  // fWereWolfAcrobatics
 | ||||||
|     150.0,  // fWereWolfAgility
 |     150.0f,  // fWereWolfAgility
 | ||||||
|     1.0,    // fWereWolfAlchemy
 |     1.0f,    // fWereWolfAlchemy
 | ||||||
|     1.0,    // fWereWolfAlteration
 |     1.0f,    // fWereWolfAlteration
 | ||||||
|     1.0,    // fWereWolfArmorer
 |     1.0f,    // fWereWolfArmorer
 | ||||||
|     150.0,  // fWereWolfAthletics
 |     150.0f,  // fWereWolfAthletics
 | ||||||
|     1.0,    // fWereWolfAxe
 |     1.0f,    // fWereWolfAxe
 | ||||||
|     1.0,    // fWereWolfBlock
 |     1.0f,    // fWereWolfBlock
 | ||||||
|     1.0,    // fWereWolfBluntWeapon
 |     1.0f,    // fWereWolfBluntWeapon
 | ||||||
|     1.0,    // fWereWolfConjuration
 |     1.0f,    // fWereWolfConjuration
 | ||||||
|     1.0,    // fWereWolfDestruction
 |     1.0f,    // fWereWolfDestruction
 | ||||||
|     1.0,    // fWereWolfEnchant
 |     1.0f,    // fWereWolfEnchant
 | ||||||
|     150.0,  // fWereWolfEndurance
 |     150.0f,  // fWereWolfEndurance
 | ||||||
|     400.0,  // fWereWolfFatigue
 |     400.0f,  // fWereWolfFatigue
 | ||||||
|     100.0,  // fWereWolfHandtoHand
 |     100.0f,  // fWereWolfHandtoHand
 | ||||||
|     2.0,    // fWereWolfHealth
 |     2.0f,    // fWereWolfHealth
 | ||||||
|     1.0,    // fWereWolfHeavyArmor
 |     1.0f,    // fWereWolfHeavyArmor
 | ||||||
|     1.0,    // fWereWolfIllusion
 |     1.0f,    // fWereWolfIllusion
 | ||||||
|     1.0,    // fWereWolfIntellegence
 |     1.0f,    // fWereWolfIntellegence
 | ||||||
|     1.0,    // fWereWolfLightArmor
 |     1.0f,    // fWereWolfLightArmor
 | ||||||
|     1.0,    // fWereWolfLongBlade
 |     1.0f,    // fWereWolfLongBlade
 | ||||||
|     1.0,    // fWereWolfLuck
 |     1.0f,    // fWereWolfLuck
 | ||||||
|     100.0,  // fWereWolfMagicka
 |     100.0f,  // fWereWolfMagicka
 | ||||||
|     1.0,    // fWereWolfMarksman
 |     1.0f,    // fWereWolfMarksman
 | ||||||
|     1.0,    // fWereWolfMediumArmor
 |     1.0f,    // fWereWolfMediumArmor
 | ||||||
|     1.0,    // fWereWolfMerchantile
 |     1.0f,    // fWereWolfMerchantile
 | ||||||
|     1.0,    // fWereWolfMysticism
 |     1.0f,    // fWereWolfMysticism
 | ||||||
|     1.0,    // fWereWolfPersonality
 |     1.0f,    // fWereWolfPersonality
 | ||||||
|     1.0,    // fWereWolfRestoration
 |     1.0f,    // fWereWolfRestoration
 | ||||||
|     1.5,    // fWereWolfRunMult
 |     1.5f,    // fWereWolfRunMult
 | ||||||
|     1.0,    // fWereWolfSecurity
 |     1.0f,    // fWereWolfSecurity
 | ||||||
|     1.0,    // fWereWolfShortBlade
 |     1.0f,    // fWereWolfShortBlade
 | ||||||
|     1.5,    // fWereWolfSilverWeaponDamageMult
 |     1.5f,    // fWereWolfSilverWeaponDamageMult
 | ||||||
|     1.0,    // fWereWolfSneak
 |     1.0f,    // fWereWolfSneak
 | ||||||
|     1.0,    // fWereWolfSpear
 |     1.0f,    // fWereWolfSpear
 | ||||||
|     1.0,    // fWereWolfSpeechcraft
 |     1.0f,    // fWereWolfSpeechcraft
 | ||||||
|     150.0,  // fWereWolfSpeed
 |     150.0f,  // fWereWolfSpeed
 | ||||||
|     150.0,  // fWereWolfStrength
 |     150.0f,  // fWereWolfStrength
 | ||||||
|     100.0,  // fWereWolfUnarmored
 |     100.0f,  // fWereWolfUnarmored
 | ||||||
|     1.0,    // fWereWolfWillPower
 |     1.0f,    // fWereWolfWillPower
 | ||||||
|     15.0   // fWortChanceValue
 |     15.0f   // fWortChanceValue
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const int CSMWorld::DefaultGmsts::IntsDefaultValues[CSMWorld::DefaultGmsts::IntCount] = | const int CSMWorld::DefaultGmsts::IntsDefaultValues[CSMWorld::DefaultGmsts::IntCount] = | ||||||
|  |  | ||||||
|  | @ -191,7 +191,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco | ||||||
| 
 | 
 | ||||||
|     if (index==-1) |     if (index==-1) | ||||||
|     { |     { | ||||||
|         int index = mIdCollection->getAppendIndex (id, type); |         index = mIdCollection->getAppendIndex (id, type); | ||||||
| 
 | 
 | ||||||
|         beginInsertRows (QModelIndex(), index, index); |         beginInsertRows (QModelIndex(), index, index); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -198,12 +198,12 @@ QModelIndex CSMWorld::IdTree::parent (const QModelIndex& index) const | ||||||
|         return QModelIndex(); |         return QModelIndex(); | ||||||
| 
 | 
 | ||||||
|     unsigned int id = index.internalId(); |     unsigned int id = index.internalId(); | ||||||
|     const std::pair<int, int>& adress(unfoldIndexAddress(id)); |     const std::pair<int, int>& address(unfoldIndexAddress(id)); | ||||||
| 
 | 
 | ||||||
|     if (adress.first >= this->rowCount() || adress.second >= this->columnCount()) |     if (address.first >= this->rowCount() || address.second >= this->columnCount()) | ||||||
|         throw "Parent index is not present in the model"; |         throw "Parent index is not present in the model"; | ||||||
| 
 | 
 | ||||||
|     return createIndex(adress.first, adress.second); |     return createIndex(address.first, address.second); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const | unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool | ||||||
|                     std::cerr << "Position: #" << index.first << " " << index.second |                     std::cerr << "Position: #" << index.first << " " << index.second | ||||||
|                         <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; |                         <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; | ||||||
| 
 | 
 | ||||||
|                     std::ostringstream stream; |                     stream.clear(); | ||||||
|                     stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; |                     stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; | ||||||
|                     ref.mCell = stream.str(); // overwrite
 |                     ref.mCell = stream.str(); // overwrite
 | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -50,7 +50,7 @@ namespace CSVRender | ||||||
|             updateCellData(mData.getCells().getRecord(cellIndex)); |             updateCellData(mData.getCells().getRecord(cellIndex)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Keep water existance/height up to date
 |         // Keep water existence/height up to date
 | ||||||
|         QAbstractItemModel* cells = mData.getTableModel(CSMWorld::UniversalId::Type_Cells); |         QAbstractItemModel* cells = mData.getTableModel(CSMWorld::UniversalId::Type_Cells); | ||||||
|         connect(cells, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), |         connect(cells, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), | ||||||
|             this, SLOT(cellDataChanged(const QModelIndex&, const QModelIndex&))); |             this, SLOT(cellDataChanged(const QModelIndex&, const QModelIndex&))); | ||||||
|  |  | ||||||
|  | @ -516,7 +516,7 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) | ||||||
|                 // Current coordinate
 |                 // Current coordinate
 | ||||||
|                 int x, y; |                 int x, y; | ||||||
| 
 | 
 | ||||||
|                 // Loop throught all the coordinates to add them to selection
 |                 // Loop through all the coordinates to add them to selection
 | ||||||
|                 while (stream >> ignore1 >> ignore2 >> x >> y) |                 while (stream >> ignore1 >> ignore2 >> x >> y) | ||||||
|                     selection.add (CSMWorld::CellCoordinates (x, y)); |                     selection.add (CSMWorld::CellCoordinates (x, y)); | ||||||
|                                 |                                 | ||||||
|  |  | ||||||
|  | @ -214,7 +214,7 @@ SceneWidget::SceneWidget(boost::shared_ptr<Resource::ResourceSystem> resourceSys | ||||||
| 
 | 
 | ||||||
| SceneWidget::~SceneWidget() | SceneWidget::~SceneWidget() | ||||||
| { | { | ||||||
|     // Since we're holding on to the scene templates past the existance of this graphics context, we'll need to manually release the created objects
 |     // Since we're holding on to the scene templates past the existence of this graphics context, we'll need to manually release the created objects
 | ||||||
|     mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); |     mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -45,8 +45,8 @@ namespace CSVWorld | ||||||
|             ///< \note Non-existent cells are not listed.
 |             ///< \note Non-existent cells are not listed.
 | ||||||
| 
 | 
 | ||||||
|             QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const; |             QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const; | ||||||
|             ///< \param existant Include existant cells.
 |             ///< \param existent Include existent cells.
 | ||||||
|             /// \param nonExistant Include non-existant cells.
 |             /// \param nonExistent Include non-existent cells.
 | ||||||
| 
 | 
 | ||||||
|             QModelIndexList getMissingRegionCells() const; |             QModelIndexList getMissingRegionCells() const; | ||||||
|             ///< Unselected cells within all regions that have at least one selected cell.
 |             ///< Unselected cells within all regions that have at least one selected cell.
 | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ add_openmw_dir (mwgui | ||||||
|     itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview |     itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview | ||||||
|     tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog |     tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog | ||||||
|     recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview |     recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview | ||||||
|     draganddrop timeadvancer jailscreen |     draganddrop timeadvancer jailscreen itemchargeview | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| add_openmw_dir (mwdialogue | add_openmw_dir (mwdialogue | ||||||
|  | @ -179,6 +179,21 @@ target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| if(APPLE) | if(APPLE) | ||||||
|  |     set(BUNDLE_RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources") | ||||||
|  | 
 | ||||||
|  |     set(OPENMW_MYGUI_FILES_ROOT ${BUNDLE_RESOURCES_DIR}) | ||||||
|  |     set(OPENMW_SHADERS_ROOT ${BUNDLE_RESOURCES_DIR}) | ||||||
|  | 
 | ||||||
|  |     add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files) | ||||||
|  | 
 | ||||||
|  |     configure_file("${OpenMW_BINARY_DIR}/settings-default.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY) | ||||||
|  |     configure_file("${OpenMW_BINARY_DIR}/openmw.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY) | ||||||
|  |     configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY) | ||||||
|  | 
 | ||||||
|  |     add_custom_command(TARGET openmw | ||||||
|  |         POST_BUILD | ||||||
|  |         COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${BUNDLE_RESOURCES_DIR}/resources") | ||||||
|  | 
 | ||||||
|     find_library(COCOA_FRAMEWORK Cocoa) |     find_library(COCOA_FRAMEWORK Cocoa) | ||||||
|     find_library(IOKIT_FRAMEWORK IOKit) |     find_library(IOKIT_FRAMEWORK IOKit) | ||||||
|     target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) |     target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) | ||||||
|  | @ -186,7 +201,7 @@ if(APPLE) | ||||||
|     if (FFmpeg_FOUND) |     if (FFmpeg_FOUND) | ||||||
|         find_library(COREVIDEO_FRAMEWORK CoreVideo) |         find_library(COREVIDEO_FRAMEWORK CoreVideo) | ||||||
|         find_library(VDA_FRAMEWORK VideoDecodeAcceleration) |         find_library(VDA_FRAMEWORK VideoDecodeAcceleration) | ||||||
|         target_link_libraries(openmw ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK}) |         target_link_libraries(openmw z ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK}) | ||||||
|     endif() |     endif() | ||||||
| endif(APPLE) | endif(APPLE) | ||||||
| 
 | 
 | ||||||
|  | @ -202,3 +217,8 @@ if (MSVC) | ||||||
|     endif (CMAKE_CL_64) |     endif (CMAKE_CL_64) | ||||||
|     add_definitions("-D_USE_MATH_DEFINES") |     add_definitions("-D_USE_MATH_DEFINES") | ||||||
| endif (MSVC) | endif (MSVC) | ||||||
|  | 
 | ||||||
|  | if (WIN32) | ||||||
|  |     INSTALL(TARGETS openmw RUNTIME DESTINATION ".") | ||||||
|  | endif (WIN32) | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -493,7 +493,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) | ||||||
|     mEnvironment.setWindowManager (window); |     mEnvironment.setWindowManager (window); | ||||||
| 
 | 
 | ||||||
|     // Create sound system
 |     // Create sound system
 | ||||||
|     mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound)); |     mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mFallbackMap, mUseSound)); | ||||||
| 
 | 
 | ||||||
|     if (!mSkipMenu) |     if (!mSkipMenu) | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -352,9 +352,8 @@ int main(int argc, char**argv) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|         // FIXME: set current dir to bundle path
 |         boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0])); | ||||||
|         //boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path();
 |         boost::filesystem::current_path(binary_path.parent_path()); | ||||||
|         //boost::filesystem::current_path(bundlePath);
 |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|         engine.reset(new OMW::Engine(cfgMgr)); |         engine.reset(new OMW::Engine(cfgMgr)); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,19 @@ | ||||||
| #include <set> | #include <set> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | namespace Loading | ||||||
|  | { | ||||||
|  |     class Listener; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace ESM | ||||||
|  | { | ||||||
|  |     class ESMReader; | ||||||
|  |     class ESMWriter; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace MWBase | namespace MWBase | ||||||
| { | { | ||||||
|     /// \brief Interface for input manager (implemented in MWInput)
 |     /// \brief Interface for input manager (implemented in MWInput)
 | ||||||
|  | @ -56,6 +69,10 @@ namespace MWBase | ||||||
|             /// Returns if the last used input device was a joystick or a keyboard
 |             /// Returns if the last used input device was a joystick or a keyboard
 | ||||||
|             /// @return true if joystick, false otherwise
 |             /// @return true if joystick, false otherwise
 | ||||||
|             virtual bool joystickLastUsed() = 0; |             virtual bool joystickLastUsed() = 0; | ||||||
|  | 
 | ||||||
|  |             virtual int countSavedGameRecords() const = 0; | ||||||
|  |             virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0; | ||||||
|  |             virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0; | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -233,7 +233,7 @@ namespace MWBase | ||||||
|             virtual void removeDialog(MWGui::Layout* dialog) = 0; |             virtual void removeDialog(MWGui::Layout* dialog) = 0; | ||||||
| 
 | 
 | ||||||
|             ///Gracefully attempts to exit the topmost GUI mode
 |             ///Gracefully attempts to exit the topmost GUI mode
 | ||||||
|             /** No guarentee of actually closing the window **/ |             /** No guarantee of actually closing the window **/ | ||||||
|             virtual void exitCurrentGuiMode() = 0; |             virtual void exitCurrentGuiMode() = 0; | ||||||
| 
 | 
 | ||||||
|             virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; |             virtual void messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; | ||||||
|  |  | ||||||
|  | @ -548,7 +548,7 @@ namespace MWBase | ||||||
|             /// Resets all actors in the current active cells to their original location within that cell.
 |             /// Resets all actors in the current active cells to their original location within that cell.
 | ||||||
|             virtual void resetActors() = 0; |             virtual void resetActors() = 0; | ||||||
| 
 | 
 | ||||||
|             virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor) = 0; |             virtual bool isWalkingOnWater (const MWWorld::ConstPtr& actor) const = 0; | ||||||
| 
 | 
 | ||||||
|             /// Return a vector aiming the actor's weapon towards a target.
 |             /// Return a vector aiming the actor's weapon towards a target.
 | ||||||
|             /// @note The length of the vector is the distance between actor and target.
 |             /// @note The length of the vector is the distance between actor and target.
 | ||||||
|  | @ -560,6 +560,12 @@ namespace MWBase | ||||||
|             virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0; |             virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0; | ||||||
| 
 | 
 | ||||||
|             virtual bool isPlayerInJail() const = 0; |             virtual bool isPlayerInJail() const = 0; | ||||||
|  | 
 | ||||||
|  |             /// Return terrain height at \a worldPos position.
 | ||||||
|  |             virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const = 0; | ||||||
|  | 
 | ||||||
|  |             /// Return physical or rendering half extents of the given actor.
 | ||||||
|  |             virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering=false) const = 0; | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -118,9 +118,9 @@ namespace MWClass | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); |             const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); | ||||||
|             MWWorld::ManualRef ref(store, id); |             MWWorld::ManualRef manualRef(store, id); | ||||||
|             ref.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition()); |             manualRef.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition()); | ||||||
|             MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition()); |             MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(manualRef.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition()); | ||||||
|             customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); |             customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); | ||||||
|             customData.mSpawn = false; |             customData.mSpawn = false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -174,7 +174,7 @@ namespace MWDialogue | ||||||
|                     executeScript (info->mResultScript); |                     executeScript (info->mResultScript); | ||||||
|                     mLastTopic = Misc::StringUtils::lowerCase(it->mId); |                     mLastTopic = Misc::StringUtils::lowerCase(it->mId); | ||||||
| 
 | 
 | ||||||
|                     // update topics again to accomodate changes resulting from executeScript
 |                     // update topics again to accommodate changes resulting from executeScript
 | ||||||
|                     updateTopics(); |                     updateTopics(); | ||||||
| 
 | 
 | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|  | @ -200,15 +200,15 @@ namespace MWGui | ||||||
|         std::set<MWMechanics::EffectKey> effectIds = mAlchemy->listEffects(); |         std::set<MWMechanics::EffectKey> effectIds = mAlchemy->listEffects(); | ||||||
|         Widgets::SpellEffectList list; |         Widgets::SpellEffectList list; | ||||||
|         unsigned int effectIndex=0; |         unsigned int effectIndex=0; | ||||||
|         for (std::set<MWMechanics::EffectKey>::iterator it = effectIds.begin(); it != effectIds.end(); ++it) |         for (std::set<MWMechanics::EffectKey>::iterator it2 = effectIds.begin(); it2 != effectIds.end(); ++it2) | ||||||
|         { |         { | ||||||
|             Widgets::SpellEffectParams params; |             Widgets::SpellEffectParams params; | ||||||
|             params.mEffectID = it->mId; |             params.mEffectID = it2->mId; | ||||||
|             const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(it->mId); |             const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(it2->mId); | ||||||
|             if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) |             if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) | ||||||
|                 params.mSkill = it->mArg; |                 params.mSkill = it2->mArg; | ||||||
|             else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) |             else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) | ||||||
|                 params.mAttribute = it->mArg; |                 params.mAttribute = it2->mArg; | ||||||
|             params.mIsConstant = true; |             params.mIsConstant = true; | ||||||
|             params.mNoTarget = true; |             params.mNoTarget = true; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -226,7 +226,7 @@ namespace MWGui | ||||||
|                 coord.top += lineHeight; |                 coord.top += lineHeight; | ||||||
| 
 | 
 | ||||||
|                 end = categories[category].spells.end(); |                 end = categories[category].spells.end(); | ||||||
|                 for (std::vector<std::string>::const_iterator it = categories[category].spells.begin(); it != end; ++it) |                 for (it = categories[category].spells.begin(); it != end; ++it) | ||||||
|                 { |                 { | ||||||
|                     const std::string &spellId = *it; |                     const std::string &spellId = *it; | ||||||
|                     spellWidget = mSpellArea->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + MyGUI::utility::toString(i)); |                     spellWidget = mSpellArea->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + MyGUI::utility::toString(i)); | ||||||
|  |  | ||||||
|  | @ -132,6 +132,12 @@ namespace MWGui | ||||||
|             mReviewDialog->configureSkills(major, minor); |             mReviewDialog->configureSkills(major, minor); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void CharacterCreation::onFrame(float duration) | ||||||
|  |     { | ||||||
|  |         if (mReviewDialog) | ||||||
|  |             mReviewDialog->onFrame(duration); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void CharacterCreation::spawnDialog(const char id) |     void CharacterCreation::spawnDialog(const char id) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|  |  | ||||||
|  | @ -50,6 +50,8 @@ namespace MWGui | ||||||
|     void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); |     void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); | ||||||
|     void configureSkills (const SkillList& major, const SkillList& minor); |     void configureSkills (const SkillList& major, const SkillList& minor); | ||||||
| 
 | 
 | ||||||
|  |     void onFrame(float duration); | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
|     osg::Group* mParent; |     osg::Group* mParent; | ||||||
|     Resource::ResourceSystem* mResourceSystem; |     Resource::ResourceSystem* mResourceSystem; | ||||||
|  |  | ||||||
|  | @ -85,8 +85,8 @@ void InventoryItemModel::update() | ||||||
| 
 | 
 | ||||||
|         if (mActor.getClass().hasInventoryStore(mActor)) |         if (mActor.getClass().hasInventoryStore(mActor)) | ||||||
|         { |         { | ||||||
|             MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor); |             MWWorld::InventoryStore& invStore = mActor.getClass().getInventoryStore(mActor); | ||||||
|             if (store.isEquipped(newItem.mBase)) |             if (invStore.isEquipped(newItem.mBase)) | ||||||
|                 newItem.mType = ItemStack::Type_Equipped; |                 newItem.mType = ItemStack::Type_Equipped; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										212
									
								
								apps/openmw/mwgui/itemchargeview.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								apps/openmw/mwgui/itemchargeview.cpp
									
									
									
									
									
										Normal 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))); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								apps/openmw/mwgui/itemchargeview.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								apps/openmw/mwgui/itemchargeview.hpp
									
									
									
									
									
										Normal 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 | ||||||
|  | @ -101,6 +101,8 @@ namespace MWGui | ||||||
|         { |         { | ||||||
|             if (mFrame) |             if (mFrame) | ||||||
|                 mFrame->setImageTexture(""); |                 mFrame->setImageTexture(""); | ||||||
|  |             if (mItemShadow) | ||||||
|  |                 mItemShadow->setImageTexture(""); | ||||||
|             mItem->setImageTexture(""); |             mItem->setImageTexture(""); | ||||||
|             mText->setCaption(""); |             mText->setCaption(""); | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|  | @ -5,6 +5,8 @@ | ||||||
| #include <MyGUI_ScrollView.h> | #include <MyGUI_ScrollView.h> | ||||||
| #include <MyGUI_Gui.h> | #include <MyGUI_Gui.h> | ||||||
| 
 | 
 | ||||||
|  | #include <components/widgets/box.hpp> | ||||||
|  | 
 | ||||||
| #include <components/misc/rng.hpp> | #include <components/misc/rng.hpp> | ||||||
| 
 | 
 | ||||||
| #include "../mwbase/world.hpp" | #include "../mwbase/world.hpp" | ||||||
|  | @ -21,6 +23,9 @@ | ||||||
| 
 | 
 | ||||||
| #include "widgets.hpp" | #include "widgets.hpp" | ||||||
| #include "itemwidget.hpp" | #include "itemwidget.hpp" | ||||||
|  | #include "itemchargeview.hpp" | ||||||
|  | #include "sortfilteritemmodel.hpp" | ||||||
|  | #include "inventoryitemmodel.hpp" | ||||||
| 
 | 
 | ||||||
| namespace MWGui | namespace MWGui | ||||||
| { | { | ||||||
|  | @ -29,13 +34,15 @@ Recharge::Recharge() | ||||||
|     : WindowBase("openmw_recharge_dialog.layout") |     : WindowBase("openmw_recharge_dialog.layout") | ||||||
| { | { | ||||||
|     getWidget(mBox, "Box"); |     getWidget(mBox, "Box"); | ||||||
|     getWidget(mView, "View"); |  | ||||||
|     getWidget(mGemBox, "GemBox"); |     getWidget(mGemBox, "GemBox"); | ||||||
|     getWidget(mGemIcon, "GemIcon"); |     getWidget(mGemIcon, "GemIcon"); | ||||||
|     getWidget(mChargeLabel, "ChargeLabel"); |     getWidget(mChargeLabel, "ChargeLabel"); | ||||||
|     getWidget(mCancelButton, "CancelButton"); |     getWidget(mCancelButton, "CancelButton"); | ||||||
| 
 | 
 | ||||||
|     mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onCancel); |     mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onCancel); | ||||||
|  |     mBox->eventItemClicked += MyGUI::newDelegate(this, &Recharge::onItemClicked); | ||||||
|  | 
 | ||||||
|  |     mBox->setDisplayMode(ItemChargeView::DisplayMode_EnchantmentCharge); | ||||||
| 
 | 
 | ||||||
|     setVisible(false); |     setVisible(false); | ||||||
| } | } | ||||||
|  | @ -43,8 +50,13 @@ Recharge::Recharge() | ||||||
| void Recharge::open() | void Recharge::open() | ||||||
| { | { | ||||||
|     center(); |     center(); | ||||||
|  | 
 | ||||||
|  |     SortFilterItemModel * model = new SortFilterItemModel(new InventoryItemModel(MWMechanics::getPlayer())); | ||||||
|  |     model->setFilter(SortFilterItemModel::Filter_OnlyRechargable); | ||||||
|  |     mBox->setModel(model); | ||||||
|  | 
 | ||||||
|     // Reset scrollbars
 |     // Reset scrollbars
 | ||||||
|     mView->setViewOffset(MyGUI::IntPoint(0, 0)); |     mBox->resetScrollbars(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Recharge::exit() | void Recharge::exit() | ||||||
|  | @ -72,66 +84,16 @@ void Recharge::updateView() | ||||||
| 
 | 
 | ||||||
|     bool toolBoxVisible = (gem.getRefData().getCount() != 0); |     bool toolBoxVisible = (gem.getRefData().getCount() != 0); | ||||||
|     mGemBox->setVisible(toolBoxVisible); |     mGemBox->setVisible(toolBoxVisible); | ||||||
|  |     mGemBox->setUserString("Hidden", toolBoxVisible ? "false" : "true"); | ||||||
| 
 | 
 | ||||||
|     bool toolBoxWasVisible = (mBox->getPosition().top != mGemBox->getPosition().top); |     mBox->update(); | ||||||
| 
 | 
 | ||||||
|     if (toolBoxVisible && !toolBoxWasVisible) |     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))); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ namespace MWGui | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| class ItemWidget; | class ItemWidget; | ||||||
|  | class ItemChargeView; | ||||||
| 
 | 
 | ||||||
| class Recharge : public WindowBase | class Recharge : public WindowBase | ||||||
| { | { | ||||||
|  | @ -25,8 +26,7 @@ public: | ||||||
|     void start (const MWWorld::Ptr& gem); |     void start (const MWWorld::Ptr& gem); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     MyGUI::Widget* mBox; |     ItemChargeView* mBox; | ||||||
|     MyGUI::ScrollView* mView; |  | ||||||
| 
 | 
 | ||||||
|     MyGUI::Widget* mGemBox; |     MyGUI::Widget* mGemBox; | ||||||
| 
 | 
 | ||||||
|  | @ -38,7 +38,7 @@ protected: | ||||||
| 
 | 
 | ||||||
|     void updateView(); |     void updateView(); | ||||||
| 
 | 
 | ||||||
|     void onItemClicked (MyGUI::Widget* sender); |     void onItemClicked (MyGUI::Widget* sender, const MWWorld::Ptr& item); | ||||||
|     void onCancel (MyGUI::Widget* sender); |     void onCancel (MyGUI::Widget* sender); | ||||||
|     void onMouseWheel(MyGUI::Widget* _sender, int _rel); |     void onMouseWheel(MyGUI::Widget* _sender, int _rel); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,9 @@ | ||||||
| 
 | 
 | ||||||
| #include <MyGUI_ScrollView.h> | #include <MyGUI_ScrollView.h> | ||||||
| #include <MyGUI_Gui.h> | #include <MyGUI_Gui.h> | ||||||
|  | #include <MyGUI_ItemBox.h> | ||||||
|  | 
 | ||||||
|  | #include <components/widgets/box.hpp> | ||||||
| 
 | 
 | ||||||
| #include "../mwbase/world.hpp" | #include "../mwbase/world.hpp" | ||||||
| #include "../mwbase/environment.hpp" | #include "../mwbase/environment.hpp" | ||||||
|  | @ -17,6 +20,9 @@ | ||||||
| #include "widgets.hpp" | #include "widgets.hpp" | ||||||
| 
 | 
 | ||||||
| #include "itemwidget.hpp" | #include "itemwidget.hpp" | ||||||
|  | #include "itemchargeview.hpp" | ||||||
|  | #include "sortfilteritemmodel.hpp" | ||||||
|  | #include "inventoryitemmodel.hpp" | ||||||
| 
 | 
 | ||||||
| namespace MWGui | namespace MWGui | ||||||
| { | { | ||||||
|  | @ -25,7 +31,6 @@ Repair::Repair() | ||||||
|     : WindowBase("openmw_repair.layout") |     : WindowBase("openmw_repair.layout") | ||||||
| { | { | ||||||
|     getWidget(mRepairBox, "RepairBox"); |     getWidget(mRepairBox, "RepairBox"); | ||||||
|     getWidget(mRepairView, "RepairView"); |  | ||||||
|     getWidget(mToolBox, "ToolBox"); |     getWidget(mToolBox, "ToolBox"); | ||||||
|     getWidget(mToolIcon, "ToolIcon"); |     getWidget(mToolIcon, "ToolIcon"); | ||||||
|     getWidget(mUsesLabel, "UsesLabel"); |     getWidget(mUsesLabel, "UsesLabel"); | ||||||
|  | @ -33,13 +38,21 @@ Repair::Repair() | ||||||
|     getWidget(mCancelButton, "CancelButton"); |     getWidget(mCancelButton, "CancelButton"); | ||||||
| 
 | 
 | ||||||
|     mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel); |     mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel); | ||||||
|  |     mRepairBox->eventItemClicked += MyGUI::newDelegate(this, &Repair::onRepairItem); | ||||||
|  | 
 | ||||||
|  |     mRepairBox->setDisplayMode(ItemChargeView::DisplayMode_Health); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Repair::open() | void Repair::open() | ||||||
| { | { | ||||||
|     center(); |     center(); | ||||||
|  | 
 | ||||||
|  |     SortFilterItemModel * model = new SortFilterItemModel(new InventoryItemModel(MWMechanics::getPlayer())); | ||||||
|  |     model->setFilter(SortFilterItemModel::Filter_OnlyRepairable); | ||||||
|  |     mRepairBox->setModel(model); | ||||||
|  | 
 | ||||||
|     // Reset scrollbars
 |     // Reset scrollbars
 | ||||||
|     mRepairView->setViewOffset(MyGUI::IntPoint(0, 0)); |     mRepairBox->resetScrollbars(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Repair::exit() | void Repair::exit() | ||||||
|  | @ -75,89 +88,31 @@ void Repair::updateRepairView() | ||||||
| 
 | 
 | ||||||
|     bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0); |     bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0); | ||||||
|     mToolBox->setVisible(toolBoxVisible); |     mToolBox->setVisible(toolBoxVisible); | ||||||
|  |     mToolBox->setUserString("Hidden", toolBoxVisible ? "false" : "true"); | ||||||
| 
 | 
 | ||||||
|     bool toolBoxWasVisible = (mRepairBox->getPosition().top != mToolBox->getPosition().top); |     mRepairBox->update(); | ||||||
| 
 | 
 | ||||||
|     if (toolBoxVisible && !toolBoxWasVisible) |     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)); |     box->notifyChildrenSizeChanged(); | ||||||
|     } |     center(); | ||||||
|     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()) | void Repair::onCancel(MyGUI::Widget* /*sender*/) | ||||||
|         MyGUI::Gui::getInstance().destroyWidget(mRepairView->getChildAt(0)); |  | ||||||
| 
 |  | ||||||
|     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) |  | ||||||
| { | { | ||||||
|     exit(); |     exit(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Repair::onRepairItem(MyGUI::Widget *sender) | void Repair::onRepairItem(MyGUI::Widget* /*sender*/, const MWWorld::Ptr& ptr) | ||||||
| { | { | ||||||
|     if (!mRepair.getTool().getRefData().getCount()) |     if (!mRepair.getTool().getRefData().getCount()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     mRepair.repair(*sender->getUserData<MWWorld::Ptr>()); |     mRepair.repair(ptr); | ||||||
| 
 | 
 | ||||||
|     updateRepairView(); |     updateRepairView(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Repair::onMouseWheel(MyGUI::Widget* _sender, int _rel) |  | ||||||
| { |  | ||||||
|     if (mRepairView->getViewOffset().top + _rel*0.3f > 0) |  | ||||||
|         mRepairView->setViewOffset(MyGUI::IntPoint(0, 0)); |  | ||||||
|     else |  | ||||||
|         mRepairView->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(mRepairView->getViewOffset().top + _rel*0.3f))); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ namespace MWGui | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| class ItemWidget; | class ItemWidget; | ||||||
|  | class ItemChargeView; | ||||||
| 
 | 
 | ||||||
| class Repair : public WindowBase | class Repair : public WindowBase | ||||||
| { | { | ||||||
|  | @ -22,8 +23,7 @@ public: | ||||||
|     void startRepairItem (const MWWorld::Ptr& item); |     void startRepairItem (const MWWorld::Ptr& item); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     MyGUI::Widget* mRepairBox; |     ItemChargeView* mRepairBox; | ||||||
|     MyGUI::ScrollView* mRepairView; |  | ||||||
| 
 | 
 | ||||||
|     MyGUI::Widget* mToolBox; |     MyGUI::Widget* mToolBox; | ||||||
| 
 | 
 | ||||||
|  | @ -38,9 +38,8 @@ protected: | ||||||
| 
 | 
 | ||||||
|     void updateRepairView(); |     void updateRepairView(); | ||||||
| 
 | 
 | ||||||
|     void onRepairItem (MyGUI::Widget* sender); |     void onRepairItem(MyGUI::Widget* sender, const MWWorld::Ptr& ptr); | ||||||
|     void onCancel(MyGUI::Widget* sender); |     void onCancel(MyGUI::Widget* sender); | ||||||
|     void onMouseWheel(MyGUI::Widget* _sender, int _rel); |  | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include "../mwbase/world.hpp" | #include "../mwbase/world.hpp" | ||||||
| #include "../mwbase/windowmanager.hpp" | #include "../mwbase/windowmanager.hpp" | ||||||
| #include "../mwworld/esmstore.hpp" | #include "../mwworld/esmstore.hpp" | ||||||
|  | #include "../mwmechanics/autocalcspell.hpp" | ||||||
| 
 | 
 | ||||||
| #include "tooltips.hpp" | #include "tooltips.hpp" | ||||||
| 
 | 
 | ||||||
|  | @ -29,7 +30,8 @@ namespace MWGui | ||||||
|     const int ReviewDialog::sLineHeight = 18; |     const int ReviewDialog::sLineHeight = 18; | ||||||
| 
 | 
 | ||||||
|     ReviewDialog::ReviewDialog() |     ReviewDialog::ReviewDialog() | ||||||
|         : WindowModal("openmw_chargen_review.layout") |         : WindowModal("openmw_chargen_review.layout"), | ||||||
|  |           mUpdateSkillArea(false) | ||||||
|     { |     { | ||||||
|         // Centre dialog
 |         // Centre dialog
 | ||||||
|         center(); |         center(); | ||||||
|  | @ -102,7 +104,16 @@ namespace MWGui | ||||||
|     void ReviewDialog::open() |     void ReviewDialog::open() | ||||||
|     { |     { | ||||||
|         WindowModal::open(); |         WindowModal::open(); | ||||||
|  |         mUpdateSkillArea = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ReviewDialog::onFrame(float /*duration*/) | ||||||
|  |     { | ||||||
|  |         if (mUpdateSkillArea) | ||||||
|  |         { | ||||||
|             updateSkillArea(); |             updateSkillArea(); | ||||||
|  |             mUpdateSkillArea = false; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ReviewDialog::setPlayerName(const std::string &name) |     void ReviewDialog::setPlayerName(const std::string &name) | ||||||
|  | @ -121,6 +132,8 @@ namespace MWGui | ||||||
|             ToolTips::createRaceToolTip(mRaceWidget, race); |             ToolTips::createRaceToolTip(mRaceWidget, race); | ||||||
|             mRaceWidget->setCaption(race->mName); |             mRaceWidget->setCaption(race->mName); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         mUpdateSkillArea = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ReviewDialog::setClass(const ESM::Class& class_) |     void ReviewDialog::setClass(const ESM::Class& class_) | ||||||
|  | @ -141,6 +154,8 @@ namespace MWGui | ||||||
|             mBirthSignWidget->setCaption(sign->mName); |             mBirthSignWidget->setCaption(sign->mName); | ||||||
|             ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); |             ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         mUpdateSkillArea = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ReviewDialog::setHealth(const MWMechanics::DynamicStat<float>& value) |     void ReviewDialog::setHealth(const MWMechanics::DynamicStat<float>& value) | ||||||
|  | @ -170,7 +185,11 @@ namespace MWGui | ||||||
|         if (attr == mAttributeWidgets.end()) |         if (attr == mAttributeWidgets.end()) | ||||||
|             return; |             return; | ||||||
| 
 | 
 | ||||||
|  |         if (attr->second->getAttributeValue() != value) | ||||||
|  |         { | ||||||
|             attr->second->setAttributeValue(value); |             attr->second->setAttributeValue(value); | ||||||
|  |             mUpdateSkillArea = true; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value) |     void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value) | ||||||
|  | @ -191,6 +210,7 @@ namespace MWGui | ||||||
|             widget->_setWidgetState(state); |             widget->_setWidgetState(state); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         mUpdateSkillArea = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor) |     void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor) | ||||||
|  | @ -211,7 +231,7 @@ namespace MWGui | ||||||
|                 mMiscSkills.push_back(skill); |                 mMiscSkills.push_back(skill); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         updateSkillArea(); |         mUpdateSkillArea = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) |     void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) | ||||||
|  | @ -245,7 +265,7 @@ namespace MWGui | ||||||
|         skillNameWidget->setCaption(text); |         skillNameWidget->setCaption(text); | ||||||
|         skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); |         skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); | ||||||
| 
 | 
 | ||||||
|         skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); |         skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Default); | ||||||
|         skillValueWidget->setCaption(value); |         skillValueWidget->setCaption(value); | ||||||
|         skillValueWidget->_setWidgetState(state); |         skillValueWidget->_setWidgetState(state); | ||||||
|         skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); |         skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); | ||||||
|  | @ -273,6 +293,20 @@ namespace MWGui | ||||||
|         coord2.top += sLineHeight; |         coord2.top += sLineHeight; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void ReviewDialog::addItem(const ESM::Spell* spell, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) | ||||||
|  |     { | ||||||
|  |         Widgets::MWSpellPtr widget = mSkillView->createWidget<Widgets::MWSpell>("MW_StatName", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); | ||||||
|  |         widget->setSpellId(spell->mId); | ||||||
|  |         widget->setUserString("ToolTipType", "Spell"); | ||||||
|  |         widget->setUserString("Spell", spell->mId); | ||||||
|  |         widget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); | ||||||
|  | 
 | ||||||
|  |         mSkillWidgets.push_back(widget); | ||||||
|  | 
 | ||||||
|  |         coord1.top += sLineHeight; | ||||||
|  |         coord2.top += sLineHeight; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) |     void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) | ||||||
|     { |     { | ||||||
|         // Add a line separator if there are items above
 |         // Add a line separator if there are items above
 | ||||||
|  | @ -332,6 +366,80 @@ namespace MWGui | ||||||
|         if (!mMiscSkills.empty()) |         if (!mMiscSkills.empty()) | ||||||
|             addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); |             addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); | ||||||
| 
 | 
 | ||||||
|  |         // starting spells
 | ||||||
|  |         std::vector<std::string> spells; | ||||||
|  | 
 | ||||||
|  |         const ESM::Race* race = NULL; | ||||||
|  |         if (!mRaceId.empty()) | ||||||
|  |             race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(mRaceId); | ||||||
|  | 
 | ||||||
|  |         int skills[ESM::Skill::Length]; | ||||||
|  |         for (int i=0; i<ESM::Skill::Length; ++i) | ||||||
|  |             skills[i] = mSkillValues.find(i)->second.getBase(); | ||||||
|  | 
 | ||||||
|  |         int attributes[ESM::Attribute::Length]; | ||||||
|  |         for (int i=0; i<ESM::Attribute::Length; ++i) | ||||||
|  |             attributes[i] = mAttributeWidgets[i]->getAttributeValue().getBase(); | ||||||
|  | 
 | ||||||
|  |         std::vector<std::string> selectedSpells = MWMechanics::autoCalcPlayerSpells(skills, attributes, race); | ||||||
|  |         for (std::vector<std::string>::iterator iter = selectedSpells.begin(); iter != selectedSpells.end(); ++iter) | ||||||
|  |         { | ||||||
|  |             std::string lower = Misc::StringUtils::lowerCase(*iter); | ||||||
|  |             if (std::find(spells.begin(), spells.end(), lower) == spells.end()) | ||||||
|  |                 spells.push_back(lower); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (race) | ||||||
|  |         { | ||||||
|  |             for (std::vector<std::string>::const_iterator iter = race->mPowers.mList.begin(); | ||||||
|  |                  iter != race->mPowers.mList.end(); ++iter) | ||||||
|  |             { | ||||||
|  |                 std::string lower = Misc::StringUtils::lowerCase(*iter); | ||||||
|  |                 if (std::find(spells.begin(), spells.end(), lower) == spells.end()) | ||||||
|  |                     spells.push_back(lower); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!mBirthSignId.empty()) | ||||||
|  |         { | ||||||
|  |             const ESM::BirthSign* sign = MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find(mBirthSignId); | ||||||
|  |             for (std::vector<std::string>::const_iterator iter = sign->mPowers.mList.begin(); | ||||||
|  |                  iter != sign->mPowers.mList.end(); ++iter) | ||||||
|  |             { | ||||||
|  |                 std::string lower = Misc::StringUtils::lowerCase(*iter); | ||||||
|  |                 if (std::find(spells.begin(), spells.end(), lower) == spells.end()) | ||||||
|  |                     spells.push_back(lower); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!mSkillWidgets.empty()) | ||||||
|  |             addSeparator(coord1, coord2); | ||||||
|  |         addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sTypeAbility", "Abilities"), coord1, coord2); | ||||||
|  |         for (std::vector<std::string>::const_iterator iter = spells.begin(); iter != spells.end(); ++iter) | ||||||
|  |         { | ||||||
|  |             const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(*iter); | ||||||
|  |             if (spell->mData.mType == ESM::Spell::ST_Ability) | ||||||
|  |                 addItem(spell, coord1, coord2); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         addSeparator(coord1, coord2); | ||||||
|  |         addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sTypePower", "Powers"), coord1, coord2); | ||||||
|  |         for (std::vector<std::string>::const_iterator iter = spells.begin(); iter != spells.end(); ++iter) | ||||||
|  |         { | ||||||
|  |             const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(*iter); | ||||||
|  |             if (spell->mData.mType == ESM::Spell::ST_Power) | ||||||
|  |                 addItem(spell, coord1, coord2); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         addSeparator(coord1, coord2); | ||||||
|  |         addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sTypeSpell", "Spells"), coord1, coord2); | ||||||
|  |         for (std::vector<std::string>::const_iterator iter = spells.begin(); iter != spells.end(); ++iter) | ||||||
|  |         { | ||||||
|  |             const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(*iter); | ||||||
|  |             if (spell->mData.mType == ESM::Spell::ST_Spell) | ||||||
|  |                 addItem(spell, coord1, coord2); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
 |         // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
 | ||||||
|         mSkillView->setVisibleVScroll(false); |         mSkillView->setVisibleVScroll(false); | ||||||
|         mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); |         mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); | ||||||
|  |  | ||||||
|  | @ -6,6 +6,11 @@ | ||||||
| #include "windowbase.hpp" | #include "windowbase.hpp" | ||||||
| #include "widgets.hpp" | #include "widgets.hpp" | ||||||
| 
 | 
 | ||||||
|  | namespace ESM | ||||||
|  | { | ||||||
|  |     struct Spell; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace MWGui | namespace MWGui | ||||||
| { | { | ||||||
|     class WindowManager; |     class WindowManager; | ||||||
|  | @ -42,6 +47,8 @@ namespace MWGui | ||||||
| 
 | 
 | ||||||
|         virtual void open(); |         virtual void open(); | ||||||
| 
 | 
 | ||||||
|  |         void onFrame(float duration); | ||||||
|  | 
 | ||||||
|         // Events
 |         // Events
 | ||||||
|         typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; |         typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; | ||||||
|         typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int; |         typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int; | ||||||
|  | @ -75,6 +82,7 @@ namespace MWGui | ||||||
|         void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); |         void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); | ||||||
|         MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); |         MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); | ||||||
|         void addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); |         void addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); | ||||||
|  |         void addItem(const ESM::Spell* spell, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); | ||||||
|         void updateSkillArea(); |         void updateSkillArea(); | ||||||
| 
 | 
 | ||||||
|         static const int sLineHeight; |         static const int sLineHeight; | ||||||
|  | @ -92,6 +100,8 @@ namespace MWGui | ||||||
|         std::string mName, mRaceId, mBirthSignId; |         std::string mName, mRaceId, mBirthSignId; | ||||||
|         ESM::Class mKlass; |         ESM::Class mKlass; | ||||||
|         std::vector<MyGUI::Widget*> mSkillWidgets; //< Skills and other information
 |         std::vector<MyGUI::Widget*> mSkillWidgets; //< Skills and other information
 | ||||||
|  | 
 | ||||||
|  |         bool mUpdateSkillArea; | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -466,9 +466,6 @@ namespace MWGui | ||||||
|         else |         else | ||||||
|             actions = MWBase::Environment::get().getInputManager()->getActionControllerSorting(); |             actions = MWBase::Environment::get().getInputManager()->getActionControllerSorting(); | ||||||
| 
 | 
 | ||||||
|         const int h = 18; |  | ||||||
|         const int w = mControlsBox->getWidth() - 28; |  | ||||||
|         int curH = 0; |  | ||||||
|         for (std::vector<int>::const_iterator it = actions.begin(); it != actions.end(); ++it) |         for (std::vector<int>::const_iterator it = actions.begin(); it != actions.end(); ++it) | ||||||
|         { |         { | ||||||
|             std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); |             std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); | ||||||
|  | @ -481,16 +478,15 @@ namespace MWGui | ||||||
|             else |             else | ||||||
|                 binding = MWBase::Environment::get().getInputManager()->getActionControllerBindingName(*it); |                 binding = MWBase::Environment::get().getInputManager()->getActionControllerBindingName(*it); | ||||||
| 
 | 
 | ||||||
|             Gui::SharedStateButton* leftText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); |             Gui::SharedStateButton* leftText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(), MyGUI::Align::Default); | ||||||
|             leftText->setCaptionWithReplacing(desc); |             leftText->setCaptionWithReplacing(desc); | ||||||
| 
 | 
 | ||||||
|             Gui::SharedStateButton* rightText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); |             Gui::SharedStateButton* rightText = mControlsBox->createWidget<Gui::SharedStateButton>("SandTextButton", MyGUI::IntCoord(), MyGUI::Align::Default); | ||||||
|             rightText->setCaptionWithReplacing(binding); |             rightText->setCaptionWithReplacing(binding); | ||||||
|             rightText->setTextAlign (MyGUI::Align::Right); |             rightText->setTextAlign (MyGUI::Align::Right); | ||||||
|             rightText->setUserData(*it); // save the action id for callbacks
 |             rightText->setUserData(*it); // save the action id for callbacks
 | ||||||
|             rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); |             rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); | ||||||
|             rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel); |             rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel); | ||||||
|             curH += h; |  | ||||||
| 
 | 
 | ||||||
|             Gui::ButtonGroup group; |             Gui::ButtonGroup group; | ||||||
|             group.push_back(leftText); |             group.push_back(leftText); | ||||||
|  | @ -498,9 +494,25 @@ namespace MWGui | ||||||
|             Gui::SharedStateButton::createButtonGroup(group); |             Gui::SharedStateButton::createButtonGroup(group); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         layoutControlsBox(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SettingsWindow::layoutControlsBox() | ||||||
|  |     { | ||||||
|  |         const int h = 18; | ||||||
|  |         const int w = mControlsBox->getWidth() - 28; | ||||||
|  |         const int noWidgetsInRow = 2; | ||||||
|  |         const int totalH = mControlsBox->getChildCount() / noWidgetsInRow * h; | ||||||
|  | 
 | ||||||
|  |         for (size_t i = 0; i < mControlsBox->getChildCount(); i++) | ||||||
|  |         { | ||||||
|  |             MyGUI::Widget * widget = mControlsBox->getChildAt(i); | ||||||
|  |             widget->setCoord(0, i / noWidgetsInRow * h, w, h); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
 |         // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
 | ||||||
|         mControlsBox->setVisibleVScroll(false); |         mControlsBox->setVisibleVScroll(false); | ||||||
|         mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); |         mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(totalH, mControlsBox->getHeight())); | ||||||
|         mControlsBox->setVisibleVScroll(true); |         mControlsBox->setVisibleVScroll(true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -556,7 +568,7 @@ namespace MWGui | ||||||
| 
 | 
 | ||||||
|     void SettingsWindow::onWindowResize(MyGUI::Window *_sender) |     void SettingsWindow::onWindowResize(MyGUI::Window *_sender) | ||||||
|     { |     { | ||||||
|         updateControlsBox(); |         layoutControlsBox(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void SettingsWindow::resetScrollbars() |     void SettingsWindow::resetScrollbars() | ||||||
|  |  | ||||||
|  | @ -67,6 +67,8 @@ namespace MWGui | ||||||
|             void configureWidgets(MyGUI::Widget* widget); |             void configureWidgets(MyGUI::Widget* widget); | ||||||
|             void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value); |             void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value); | ||||||
| 
 | 
 | ||||||
|  |             void layoutControlsBox(); | ||||||
|  |          | ||||||
|         private: |         private: | ||||||
|             void resetScrollbars(); |             void resetScrollbars(); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| #include "sortfilteritemmodel.hpp" | #include "sortfilteritemmodel.hpp" | ||||||
| 
 | 
 | ||||||
|  | #include <iostream> | ||||||
|  | 
 | ||||||
| #include <components/misc/stringops.hpp> | #include <components/misc/stringops.hpp> | ||||||
| 
 | 
 | ||||||
| #include <components/esm/loadalch.hpp> | #include <components/esm/loadalch.hpp> | ||||||
|  | @ -14,9 +16,14 @@ | ||||||
| #include <components/esm/loadprob.hpp> | #include <components/esm/loadprob.hpp> | ||||||
| #include <components/esm/loadrepa.hpp> | #include <components/esm/loadrepa.hpp> | ||||||
| #include <components/esm/loadweap.hpp> | #include <components/esm/loadweap.hpp> | ||||||
|  | #include <components/esm/loadench.hpp> | ||||||
|  | 
 | ||||||
|  | #include "../mwbase/environment.hpp" | ||||||
|  | #include "../mwbase/world.hpp" | ||||||
| 
 | 
 | ||||||
| #include "../mwworld/class.hpp" | #include "../mwworld/class.hpp" | ||||||
| #include "../mwworld/nullaction.hpp" | #include "../mwworld/nullaction.hpp" | ||||||
|  | #include "../mwworld/esmstore.hpp" | ||||||
| 
 | 
 | ||||||
| namespace | namespace | ||||||
| { | { | ||||||
|  | @ -139,6 +146,31 @@ namespace MWGui | ||||||
|                 return false; |                 return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if ((mFilter & Filter_OnlyRepairable) && ( | ||||||
|  |                     !base.getClass().hasItemHealth(base) | ||||||
|  |                     || (base.getClass().getItemHealth(base) == base.getClass().getItemMaxHealth(base)) | ||||||
|  |                     || (base.getTypeName() != typeid(ESM::Weapon).name() | ||||||
|  |                         && base.getTypeName() != typeid(ESM::Armor).name()))) | ||||||
|  |             return false; | ||||||
|  | 
 | ||||||
|  |         if (mFilter & Filter_OnlyRechargable) | ||||||
|  |         { | ||||||
|  |             if (!(item.mFlags & ItemStack::Flag_Enchanted)) | ||||||
|  |                 return false; | ||||||
|  | 
 | ||||||
|  |             std::string enchId = base.getClass().getEnchantment(base); | ||||||
|  |             const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchId); | ||||||
|  |             if (!ench) | ||||||
|  |             { | ||||||
|  |                 std::cerr << "Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId() << std::endl; | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (base.getCellRef().getEnchantmentCharge() >= ench->mData.mCharge | ||||||
|  |                     || base.getCellRef().getEnchantmentCharge() == -1) | ||||||
|  |                 return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -39,6 +39,8 @@ namespace MWGui | ||||||
|         static const int Filter_OnlyEnchantable = (1<<2); |         static const int Filter_OnlyEnchantable = (1<<2); | ||||||
|         static const int Filter_OnlyChargedSoulstones = (1<<3); |         static const int Filter_OnlyChargedSoulstones = (1<<3); | ||||||
|         static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action
 |         static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action
 | ||||||
|  |         static const int Filter_OnlyRepairable = (1<<5); | ||||||
|  |         static const int Filter_OnlyRechargable = (1<<6); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| #include "../mwworld/inventorystore.hpp" | #include "../mwworld/inventorystore.hpp" | ||||||
| #include "../mwworld/class.hpp" | #include "../mwworld/class.hpp" | ||||||
| #include "../mwworld/esmstore.hpp" | #include "../mwworld/esmstore.hpp" | ||||||
|  | #include "../mwworld/player.hpp" | ||||||
| 
 | 
 | ||||||
| #include "../mwmechanics/spellcasting.hpp" | #include "../mwmechanics/spellcasting.hpp" | ||||||
| #include "../mwmechanics/spells.hpp" | #include "../mwmechanics/spells.hpp" | ||||||
|  | @ -122,8 +123,15 @@ namespace MWGui | ||||||
|         const ESM::Spell* spell = |         const ESM::Spell* spell = | ||||||
|             MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId); |             MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId); | ||||||
| 
 | 
 | ||||||
|         if (spell->mData.mFlags & ESM::Spell::F_Always |         MWWorld::Ptr player = MWMechanics::getPlayer(); | ||||||
|             || spell->mData.mType == ESM::Spell::ST_Power) |         std::string raceId = player.get<ESM::NPC>()->mBase->mRace; | ||||||
|  |         const std::string& signId = | ||||||
|  |             MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); | ||||||
|  |         const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceId); | ||||||
|  |         const ESM::BirthSign* birthsign = MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find(signId); | ||||||
|  | 
 | ||||||
|  |         // can't delete racial spells, birthsign spells or powers 
 | ||||||
|  |         if (race->mPowers.exists(spell->mId) || birthsign->mPowers.exists(spell->mId) || spell->mData.mType == ESM::Spell::ST_Power) | ||||||
|         { |         { | ||||||
|             MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}"); |             MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -256,7 +256,7 @@ namespace MWGui | ||||||
|                         std::string key = it->first.substr(0, underscorePos); |                         std::string key = it->first.substr(0, underscorePos); | ||||||
|                         std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); |                         std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); | ||||||
| 
 | 
 | ||||||
|                         std::string type = "Property"; |                         type = "Property"; | ||||||
|                         size_t caretPos = key.find("^"); |                         size_t caretPos = key.find("^"); | ||||||
|                         if (caretPos != std::string::npos) |                         if (caretPos != std::string::npos) | ||||||
|                         { |                         { | ||||||
|  |  | ||||||
|  | @ -109,6 +109,7 @@ | ||||||
| #include "container.hpp" | #include "container.hpp" | ||||||
| #include "controllers.hpp" | #include "controllers.hpp" | ||||||
| #include "jailscreen.hpp" | #include "jailscreen.hpp" | ||||||
|  | #include "itemchargeview.hpp" | ||||||
| 
 | 
 | ||||||
| namespace MWGui | namespace MWGui | ||||||
| { | { | ||||||
|  | @ -223,6 +224,7 @@ namespace MWGui | ||||||
|         MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>("Layer"); |         MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>("Layer"); | ||||||
|         BookPage::registerMyGUIComponents (); |         BookPage::registerMyGUIComponents (); | ||||||
|         ItemView::registerComponents(); |         ItemView::registerComponents(); | ||||||
|  |         ItemChargeView::registerComponents(); | ||||||
|         ItemWidget::registerComponents(); |         ItemWidget::registerComponents(); | ||||||
|         SpellView::registerComponents(); |         SpellView::registerComponents(); | ||||||
|         Gui::registerAllWidgets(); |         Gui::registerAllWidgets(); | ||||||
|  | @ -1004,6 +1006,9 @@ namespace MWGui | ||||||
|         mScreenFader->update(frameDuration); |         mScreenFader->update(frameDuration); | ||||||
| 
 | 
 | ||||||
|         mDebugWindow->onFrame(frameDuration); |         mDebugWindow->onFrame(frameDuration); | ||||||
|  | 
 | ||||||
|  |         if (mCharGen) | ||||||
|  |             mCharGen->onFrame(frameDuration); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void WindowManager::changeCell(const MWWorld::CellStore* cell) |     void WindowManager::changeCell(const MWWorld::CellStore* cell) | ||||||
|  |  | ||||||
|  | @ -16,6 +16,9 @@ | ||||||
| 
 | 
 | ||||||
| #include <components/sdlutil/sdlinputwrapper.hpp> | #include <components/sdlutil/sdlinputwrapper.hpp> | ||||||
| #include <components/sdlutil/sdlvideowrapper.hpp> | #include <components/sdlutil/sdlvideowrapper.hpp> | ||||||
|  | #include <components/esm/esmwriter.hpp> | ||||||
|  | #include <components/esm/esmreader.hpp> | ||||||
|  | #include <components/esm/controlsstate.hpp> | ||||||
| 
 | 
 | ||||||
| #include "../mwbase/world.hpp" | #include "../mwbase/world.hpp" | ||||||
| #include "../mwbase/windowmanager.hpp" | #include "../mwbase/windowmanager.hpp" | ||||||
|  | @ -1574,6 +1577,44 @@ namespace MWInput | ||||||
|             mInputBinder->removeJoystickButtonBinding (mFakeDeviceID, mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE)); |             mInputBinder->removeJoystickButtonBinding (mFakeDeviceID, mInputBinder->getJoystickButtonBinding (control, mFakeDeviceID, ICS::Control::INCREASE)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     int InputManager::countSavedGameRecords() const | ||||||
|  |     { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void InputManager::write(ESM::ESMWriter& writer, Loading::Listener& /*progress*/) | ||||||
|  |     { | ||||||
|  |         ESM::ControlsState controls; | ||||||
|  |         controls.mViewSwitchDisabled = !getControlSwitch("playerviewswitch"); | ||||||
|  |         controls.mControlsDisabled = !getControlSwitch("playercontrols"); | ||||||
|  |         controls.mJumpingDisabled = !getControlSwitch("playerjumping"); | ||||||
|  |         controls.mLookingDisabled = !getControlSwitch("playerlooking"); | ||||||
|  |         controls.mVanityModeDisabled = !getControlSwitch("vanitymode"); | ||||||
|  |         controls.mWeaponDrawingDisabled = !getControlSwitch("playerfighting"); | ||||||
|  |         controls.mSpellDrawingDisabled = !getControlSwitch("playermagic"); | ||||||
|  | 
 | ||||||
|  |         writer.startRecord (ESM::REC_INPU); | ||||||
|  |         controls.save(writer); | ||||||
|  |         writer.endRecord (ESM::REC_INPU); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void InputManager::readRecord(ESM::ESMReader& reader, uint32_t type) | ||||||
|  |     { | ||||||
|  |         if (type == ESM::REC_INPU) | ||||||
|  |         { | ||||||
|  |             ESM::ControlsState controls; | ||||||
|  |             controls.load(reader); | ||||||
|  | 
 | ||||||
|  |             toggleControlSwitch("playerviewswitch", !controls.mViewSwitchDisabled); | ||||||
|  |             toggleControlSwitch("playercontrols", !controls.mControlsDisabled); | ||||||
|  |             toggleControlSwitch("playerjumping", !controls.mJumpingDisabled); | ||||||
|  |             toggleControlSwitch("playerlooking", !controls.mLookingDisabled); | ||||||
|  |             toggleControlSwitch("vanitymode", !controls.mVanityModeDisabled); | ||||||
|  |             toggleControlSwitch("playerfighting", !controls.mWeaponDrawingDisabled); | ||||||
|  |             toggleControlSwitch("playermagic", !controls.mSpellDrawingDisabled); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void InputManager::resetToDefaultKeyBindings() |     void InputManager::resetToDefaultKeyBindings() | ||||||
|     { |     { | ||||||
|         loadKeyDefaults(true); |         loadKeyDefaults(true); | ||||||
|  |  | ||||||
|  | @ -149,6 +149,10 @@ namespace MWInput | ||||||
|         void clearAllKeyBindings (ICS::Control* control); |         void clearAllKeyBindings (ICS::Control* control); | ||||||
|         void clearAllControllerBindings (ICS::Control* control); |         void clearAllControllerBindings (ICS::Control* control); | ||||||
| 
 | 
 | ||||||
|  |         virtual int countSavedGameRecords() const; | ||||||
|  |         virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress); | ||||||
|  |         virtual void readRecord(ESM::ESMReader& reader, uint32_t type); | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
|         SDL_Window* mWindow; |         SDL_Window* mWindow; | ||||||
|         bool mWindowVisible; |         bool mWindowVisible; | ||||||
|  |  | ||||||
|  | @ -512,7 +512,7 @@ namespace MWMechanics | ||||||
|                 if (magnitude > 0 && remainingTime > 0 && remainingTime < mDuration) |                 if (magnitude > 0 && remainingTime > 0 && remainingTime < mDuration) | ||||||
|                 { |                 { | ||||||
|                     CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); |                     CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); | ||||||
|                     effectTick(creatureStats, mActor, key, magnitude * remainingTime); |                     if (effectTick(creatureStats, mActor, key, magnitude * remainingTime)) | ||||||
|                         creatureStats.getMagicEffects().add(key, -magnitude); |                         creatureStats.getMagicEffects().add(key, -magnitude); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -527,8 +527,10 @@ namespace MWMechanics | ||||||
| 
 | 
 | ||||||
|         if (duration > 0) |         if (duration > 0) | ||||||
|         { |         { | ||||||
|             // apply correct magnitude for tickable effects that have just expired,
 |             // Apply correct magnitude for tickable effects that have just expired,
 | ||||||
|             // in case duration > remaining time of effect
 |             // in case duration > remaining time of effect.
 | ||||||
|  |             // One case where this will happen is when the player uses the rest/wait command
 | ||||||
|  |             // while there is a tickable effect active that should expire before the end of the rest/wait.
 | ||||||
|             ExpiryVisitor visitor(ptr, duration); |             ExpiryVisitor visitor(ptr, duration); | ||||||
|             creatureStats.getActiveSpells().visitEffectSources(visitor); |             creatureStats.getActiveSpells().visitEffectSources(visitor); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -41,9 +41,7 @@ namespace MWMechanics | ||||||
|         if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range
 |         if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range
 | ||||||
|         { |         { | ||||||
|             // activate when reached
 |             // activate when reached
 | ||||||
|             MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); |  | ||||||
|             MWBase::Environment::get().getWorld()->activate(target, actor); |             MWBase::Environment::get().getWorld()->activate(target, actor); | ||||||
| 
 |  | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ | ||||||
| #include "character.hpp" | #include "character.hpp" | ||||||
| #include "aicombataction.hpp" | #include "aicombataction.hpp" | ||||||
| #include "combat.hpp" | #include "combat.hpp" | ||||||
|  | #include "coordinateconverter.hpp" | ||||||
| 
 | 
 | ||||||
| namespace | namespace | ||||||
| { | { | ||||||
|  | @ -51,22 +52,39 @@ namespace MWMechanics | ||||||
|         ESM::Position mShortcutFailPos; |         ESM::Position mShortcutFailPos; | ||||||
|         MWMechanics::Movement mMovement; |         MWMechanics::Movement mMovement; | ||||||
| 
 | 
 | ||||||
|  |         enum FleeState | ||||||
|  |         { | ||||||
|  |             FleeState_None, | ||||||
|  |             FleeState_Idle, | ||||||
|  |             FleeState_RunBlindly, | ||||||
|  |             FleeState_RunToDestination | ||||||
|  |         }; | ||||||
|  |         FleeState mFleeState; | ||||||
|  |         bool mLOS; | ||||||
|  |         float mUpdateLOSTimer; | ||||||
|  |         float mFleeBlindRunTimer; | ||||||
|  |         ESM::Pathgrid::Point mFleeDest; | ||||||
|  |          | ||||||
|         AiCombatStorage(): |         AiCombatStorage(): | ||||||
|         mAttackCooldown(0), |         mAttackCooldown(0.0f), | ||||||
|         mTimerReact(AI_REACTION_TIME), |         mTimerReact(AI_REACTION_TIME), | ||||||
|         mTimerCombatMove(0), |         mTimerCombatMove(0.0f), | ||||||
|         mReadyToAttack(false), |         mReadyToAttack(false), | ||||||
|         mAttack(false), |         mAttack(false), | ||||||
|         mAttackRange(0), |         mAttackRange(0.0f), | ||||||
|         mCombatMove(false), |         mCombatMove(false), | ||||||
|         mLastTargetPos(0,0,0), |         mLastTargetPos(0,0,0), | ||||||
|         mCell(NULL), |         mCell(NULL), | ||||||
|         mCurrentAction(), |         mCurrentAction(), | ||||||
|         mActionCooldown(0), |         mActionCooldown(0.0f), | ||||||
|         mStrength(), |         mStrength(), | ||||||
|         mForceNoShortcut(false), |         mForceNoShortcut(false), | ||||||
|         mShortcutFailPos(), |         mShortcutFailPos(), | ||||||
|         mMovement() |         mMovement(), | ||||||
|  |         mFleeState(FleeState_None), | ||||||
|  |         mLOS(false), | ||||||
|  |         mUpdateLOSTimer(0.0f), | ||||||
|  |         mFleeBlindRunTimer(0.0f) | ||||||
|         {} |         {} | ||||||
| 
 | 
 | ||||||
|         void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); |         void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); | ||||||
|  | @ -76,6 +94,10 @@ namespace MWMechanics | ||||||
|             const ESM::Weapon* weapon, bool distantCombat); |             const ESM::Weapon* weapon, bool distantCombat); | ||||||
|         void updateAttack(CharacterController& characterController); |         void updateAttack(CharacterController& characterController); | ||||||
|         void stopAttack(); |         void stopAttack(); | ||||||
|  | 
 | ||||||
|  |         void startFleeing(); | ||||||
|  |         void stopFleeing(); | ||||||
|  |         bool isFleeing(); | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     AiCombat::AiCombat(const MWWorld::Ptr& actor) : |     AiCombat::AiCombat(const MWWorld::Ptr& actor) : | ||||||
|  | @ -157,16 +179,27 @@ namespace MWMechanics | ||||||
|                 || target.getClass().getCreatureStats(target).isDead()) |                 || target.getClass().getCreatureStats(target).isDead()) | ||||||
|             return true; |             return true; | ||||||
| 
 | 
 | ||||||
|         if (storage.mCurrentAction.get()) // need to wait to init action with it's attack range
 |         if (!storage.isFleeing()) | ||||||
|         { |         { | ||||||
|             //Update every frame
 |             if (storage.mCurrentAction.get()) // need to wait to init action with its attack range
 | ||||||
|             bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, storage.mAttackRange); |             { | ||||||
|  |                 //Update every frame. UpdateLOS uses a timer, so the LOS check does not happen every frame.
 | ||||||
|  |                 updateLOS(actor, target, duration, storage); | ||||||
|  |                 float targetReachedTolerance = 0.0f; | ||||||
|  |                 if (storage.mLOS) | ||||||
|  |                     targetReachedTolerance = storage.mAttackRange; | ||||||
|  |                 bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, targetReachedTolerance); | ||||||
|                 if (is_target_reached) storage.mReadyToAttack = true; |                 if (is_target_reached) storage.mReadyToAttack = true; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             storage.updateCombatMove(duration); |             storage.updateCombatMove(duration); | ||||||
|             if (storage.mReadyToAttack) updateActorsMovement(actor, duration, storage); |             if (storage.mReadyToAttack) updateActorsMovement(actor, duration, storage); | ||||||
|             storage.updateAttack(characterController); |             storage.updateAttack(characterController); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             updateFleeing(actor, target, duration, storage); | ||||||
|  |         } | ||||||
|         storage.mActionCooldown -= duration; |         storage.mActionCooldown -= duration; | ||||||
| 
 | 
 | ||||||
|         float& timerReact = storage.mTimerReact; |         float& timerReact = storage.mTimerReact; | ||||||
|  | @ -185,12 +218,6 @@ namespace MWMechanics | ||||||
| 
 | 
 | ||||||
|     void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) |     void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) | ||||||
|     { |     { | ||||||
|         if (isTargetMagicallyHidden(target)) |  | ||||||
|         { |  | ||||||
|             storage.stopAttack(); |  | ||||||
|             return; // TODO: run away instead of doing nothing
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const MWWorld::CellStore*& currentCell = storage.mCell; |         const MWWorld::CellStore*& currentCell = storage.mCell; | ||||||
|         bool cellChange = currentCell && (actor.getCell() != currentCell); |         bool cellChange = currentCell && (actor.getCell() != currentCell); | ||||||
|         if(!currentCell || cellChange) |         if(!currentCell || cellChange) | ||||||
|  | @ -198,29 +225,60 @@ namespace MWMechanics | ||||||
|             currentCell = actor.getCell(); |             currentCell = actor.getCell(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         bool forceFlee = false; | ||||||
|  |         if (!canFight(actor, target)) | ||||||
|  |         { | ||||||
|  |             storage.stopAttack(); | ||||||
|  |             characterController.setAttackingOrSpell(false); | ||||||
|  |             storage.mActionCooldown = 0.f; | ||||||
|  |             forceFlee = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         const MWWorld::Class& actorClass = actor.getClass(); |         const MWWorld::Class& actorClass = actor.getClass(); | ||||||
|         actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); |         actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); | ||||||
| 
 | 
 | ||||||
|         float& actionCooldown = storage.mActionCooldown; |         float& actionCooldown = storage.mActionCooldown; | ||||||
|  |         boost::shared_ptr<Action>& currentAction = storage.mCurrentAction; | ||||||
|  | 
 | ||||||
|  |         if (!forceFlee) | ||||||
|  |         { | ||||||
|             if (actionCooldown > 0) |             if (actionCooldown > 0) | ||||||
|                 return; |                 return; | ||||||
| 
 | 
 | ||||||
|         float &rangeAttack = storage.mAttackRange; |  | ||||||
|         boost::shared_ptr<Action>& currentAction = storage.mCurrentAction; |  | ||||||
|             if (characterController.readyToPrepareAttack()) |             if (characterController.readyToPrepareAttack()) | ||||||
|             { |             { | ||||||
|                 currentAction = prepareNextAction(actor, target); |                 currentAction = prepareNextAction(actor, target); | ||||||
|                 actionCooldown = currentAction->getActionCooldown(); |                 actionCooldown = currentAction->getActionCooldown(); | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|         const ESM::Weapon *weapon = NULL; |  | ||||||
|         bool isRangedCombat = false; |  | ||||||
|         if (currentAction.get()) |  | ||||||
|         { |  | ||||||
|             rangeAttack = currentAction->getCombatRange(isRangedCombat); |  | ||||||
|             // Get weapon characteristics
 |  | ||||||
|             weapon = currentAction->getWeapon(); |  | ||||||
|         } |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             currentAction.reset(new ActionFlee()); | ||||||
|  |             actionCooldown = currentAction->getActionCooldown(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!currentAction) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         if (storage.isFleeing() != currentAction->isFleeing()) | ||||||
|  |         { | ||||||
|  |             if (currentAction->isFleeing()) | ||||||
|  |             { | ||||||
|  |                 storage.startFleeing(); | ||||||
|  |                 MWBase::Environment::get().getDialogueManager()->say(actor, "flee"); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 storage.stopFleeing(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool isRangedCombat = false; | ||||||
|  |         float &rangeAttack = storage.mAttackRange; | ||||||
|  | 
 | ||||||
|  |         rangeAttack = currentAction->getCombatRange(isRangedCombat); | ||||||
|  | 
 | ||||||
|  |         // 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()); | ||||||
|  | @ -229,19 +287,7 @@ namespace MWMechanics | ||||||
|         osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); |         osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); | ||||||
|         float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); |         float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); | ||||||
| 
 | 
 | ||||||
|         if (!currentAction) |         storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS); | ||||||
|             return; |  | ||||||
| 
 |  | ||||||
|         storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack); |  | ||||||
|          |  | ||||||
|         // can't fight if attacker can't go where target is.  E.g. A fish can't attack person on land.
 |  | ||||||
|         if (distToTarget > rangeAttack |  | ||||||
|                 && !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target)) |  | ||||||
|         { |  | ||||||
|             // TODO: start fleeing?
 |  | ||||||
|             storage.stopAttack(); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if (storage.mReadyToAttack) |         if (storage.mReadyToAttack) | ||||||
|         { |         { | ||||||
|  | @ -267,6 +313,111 @@ namespace MWMechanics | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) | ||||||
|  |     { | ||||||
|  |         static const float LOS_UPDATE_DURATION = 0.5f; | ||||||
|  |         if (storage.mUpdateLOSTimer <= 0.f) | ||||||
|  |         { | ||||||
|  |             storage.mLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); | ||||||
|  |             storage.mUpdateLOSTimer = LOS_UPDATE_DURATION; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             storage.mUpdateLOSTimer -= duration; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) | ||||||
|  |     { | ||||||
|  |         static const float BLIND_RUN_DURATION = 1.0f; | ||||||
|  | 
 | ||||||
|  |         updateLOS(actor, target, duration, storage); | ||||||
|  | 
 | ||||||
|  |         AiCombatStorage::FleeState& state = storage.mFleeState; | ||||||
|  |         switch (state) | ||||||
|  |         { | ||||||
|  |             case AiCombatStorage::FleeState_None: | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             case AiCombatStorage::FleeState_Idle: | ||||||
|  |                 { | ||||||
|  |                     float triggerDist = getMaxAttackDistance(target); | ||||||
|  | 
 | ||||||
|  |                     if (storage.mLOS && | ||||||
|  |                             (triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist)) | ||||||
|  |                     { | ||||||
|  |                         const ESM::Pathgrid* pathgrid = | ||||||
|  |                                 MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*storage.mCell->getCell()); | ||||||
|  | 
 | ||||||
|  |                         bool runFallback = true; | ||||||
|  | 
 | ||||||
|  |                         if (pathgrid && !actor.getClass().isPureWaterCreature(actor)) | ||||||
|  |                         { | ||||||
|  |                             ESM::Pathgrid::PointList points; | ||||||
|  |                             CoordinateConverter coords(storage.mCell->getCell()); | ||||||
|  | 
 | ||||||
|  |                             osg::Vec3f localPos = actor.getRefData().getPosition().asVec3(); | ||||||
|  |                             coords.toLocal(localPos); | ||||||
|  | 
 | ||||||
|  |                             int closestPointIndex = PathFinder::GetClosestPoint(pathgrid, localPos); | ||||||
|  |                             for (int i = 0; i < static_cast<int>(pathgrid->mPoints.size()); i++) | ||||||
|  |                             { | ||||||
|  |                                 if (i != closestPointIndex && storage.mCell->isPointConnected(closestPointIndex, i)) | ||||||
|  |                                 { | ||||||
|  |                                     points.push_back(pathgrid->mPoints[static_cast<size_t>(i)]); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  | 
 | ||||||
|  |                             if (!points.empty()) | ||||||
|  |                             { | ||||||
|  |                                 ESM::Pathgrid::Point dest = points[Misc::Rng::rollDice(points.size())]; | ||||||
|  |                                 coords.toWorld(dest); | ||||||
|  | 
 | ||||||
|  |                                 state = AiCombatStorage::FleeState_RunToDestination; | ||||||
|  |                                 storage.mFleeDest = ESM::Pathgrid::Point(dest.mX, dest.mY, dest.mZ); | ||||||
|  | 
 | ||||||
|  |                                 runFallback = false; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         if (runFallback) | ||||||
|  |                         { | ||||||
|  |                             state = AiCombatStorage::FleeState_RunBlindly; | ||||||
|  |                             storage.mFleeBlindRunTimer = 0.0f; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case AiCombatStorage::FleeState_RunBlindly: | ||||||
|  |                 { | ||||||
|  |                     // timer to prevent twitchy movement that can be observed in vanilla MW
 | ||||||
|  |                     if (storage.mFleeBlindRunTimer < BLIND_RUN_DURATION) | ||||||
|  |                     { | ||||||
|  |                         storage.mFleeBlindRunTimer += duration; | ||||||
|  | 
 | ||||||
|  |                         storage.mMovement.mRotation[2] = osg::PI + getZAngleToDir(target.getRefData().getPosition().asVec3()-actor.getRefData().getPosition().asVec3()); | ||||||
|  |                         storage.mMovement.mPosition[1] = 1; | ||||||
|  |                         updateActorsMovement(actor, duration, storage); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                         state = AiCombatStorage::FleeState_Idle; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case AiCombatStorage::FleeState_RunToDestination: | ||||||
|  |                 { | ||||||
|  |                     static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fFleeDistance")->getFloat(); | ||||||
|  | 
 | ||||||
|  |                     float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length(); | ||||||
|  |                     if ((dist > fFleeDistance && !storage.mLOS) | ||||||
|  |                             || pathTo(actor, storage.mFleeDest, duration)) | ||||||
|  |                     { | ||||||
|  |                         state = AiCombatStorage::FleeState_Idle; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage) |     void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage) | ||||||
|     { |     { | ||||||
|         // apply combat movement
 |         // apply combat movement
 | ||||||
|  | @ -446,6 +597,26 @@ namespace MWMechanics | ||||||
|         mReadyToAttack = false; |         mReadyToAttack = false; | ||||||
|         mAttack = false; |         mAttack = false; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     void AiCombatStorage::startFleeing() | ||||||
|  |     { | ||||||
|  |         stopFleeing(); | ||||||
|  |         mFleeState = FleeState_Idle; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void AiCombatStorage::stopFleeing() | ||||||
|  |     { | ||||||
|  |         mMovement.mPosition[0] = 0; | ||||||
|  |         mMovement.mPosition[1] = 0; | ||||||
|  |         mMovement.mPosition[2] = 0; | ||||||
|  |         mFleeState = FleeState_None; | ||||||
|  |         mFleeDest = ESM::Pathgrid::Point(0, 0, 0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool AiCombatStorage::isFleeing() | ||||||
|  |     { | ||||||
|  |         return mFleeState != FleeState_None; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -61,6 +61,10 @@ namespace MWMechanics | ||||||
| 
 | 
 | ||||||
|             void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); |             void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); | ||||||
| 
 | 
 | ||||||
|  |             void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage); | ||||||
|  | 
 | ||||||
|  |             void updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage); | ||||||
|  | 
 | ||||||
|             /// Transfer desired movement (from AiCombatStorage) to Actor
 |             /// Transfer desired movement (from AiCombatStorage) to Actor
 | ||||||
|             void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage); |             void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage); | ||||||
|             void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis,  |             void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis,  | ||||||
|  |  | ||||||
|  | @ -5,14 +5,17 @@ | ||||||
| 
 | 
 | ||||||
| #include "../mwbase/environment.hpp" | #include "../mwbase/environment.hpp" | ||||||
| #include "../mwbase/world.hpp" | #include "../mwbase/world.hpp" | ||||||
|  | #include "../mwbase/mechanicsmanager.hpp" | ||||||
| 
 | 
 | ||||||
| #include "../mwworld/class.hpp" | #include "../mwworld/class.hpp" | ||||||
| #include "../mwworld/esmstore.hpp" | #include "../mwworld/esmstore.hpp" | ||||||
| #include "../mwworld/inventorystore.hpp" | #include "../mwworld/inventorystore.hpp" | ||||||
| #include "../mwworld/actionequip.hpp" | #include "../mwworld/actionequip.hpp" | ||||||
|  | #include "../mwworld/cellstore.hpp" | ||||||
| 
 | 
 | ||||||
| #include "npcstats.hpp" | #include "npcstats.hpp" | ||||||
| #include "spellcasting.hpp" | #include "spellcasting.hpp" | ||||||
|  | #include "combat.hpp" | ||||||
| 
 | 
 | ||||||
| namespace | namespace | ||||||
| { | { | ||||||
|  | @ -517,6 +520,7 @@ namespace MWMechanics | ||||||
|         Spells& spells = actor.getClass().getCreatureStats(actor).getSpells(); |         Spells& spells = actor.getClass().getCreatureStats(actor).getSpells(); | ||||||
| 
 | 
 | ||||||
|         float bestActionRating = 0.f; |         float bestActionRating = 0.f; | ||||||
|  |         float antiFleeRating = 0.f; | ||||||
|         // Default to hand-to-hand combat
 |         // Default to hand-to-hand combat
 | ||||||
|         boost::shared_ptr<Action> bestAction (new ActionWeapon(MWWorld::Ptr())); |         boost::shared_ptr<Action> bestAction (new ActionWeapon(MWWorld::Ptr())); | ||||||
|         if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) |         if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) | ||||||
|  | @ -536,6 +540,7 @@ namespace MWMechanics | ||||||
|                 { |                 { | ||||||
|                     bestActionRating = rating; |                     bestActionRating = rating; | ||||||
|                     bestAction.reset(new ActionPotion(*it)); |                     bestAction.reset(new ActionPotion(*it)); | ||||||
|  |                     antiFleeRating = std::numeric_limits<float>::max(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -546,6 +551,7 @@ namespace MWMechanics | ||||||
|                 { |                 { | ||||||
|                     bestActionRating = rating; |                     bestActionRating = rating; | ||||||
|                     bestAction.reset(new ActionEnchantedItem(it)); |                     bestAction.reset(new ActionEnchantedItem(it)); | ||||||
|  |                     antiFleeRating = std::numeric_limits<float>::max(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -593,6 +599,7 @@ namespace MWMechanics | ||||||
| 
 | 
 | ||||||
|                     bestActionRating = rating; |                     bestActionRating = rating; | ||||||
|                     bestAction.reset(new ActionWeapon(*it, ammo)); |                     bestAction.reset(new ActionWeapon(*it, ammo)); | ||||||
|  |                     antiFleeRating = vanillaRateWeaponAndAmmo(*it, ammo, actor, enemy); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -606,13 +613,308 @@ namespace MWMechanics | ||||||
|             { |             { | ||||||
|                 bestActionRating = rating; |                 bestActionRating = rating; | ||||||
|                 bestAction.reset(new ActionSpell(spell->mId)); |                 bestAction.reset(new ActionSpell(spell->mId)); | ||||||
|  |                 antiFleeRating = vanillaRateSpell(spell, actor, enemy); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (makeFleeDecision(actor, enemy, antiFleeRating)) | ||||||
|  |             bestAction.reset(new ActionFlee()); | ||||||
|  | 
 | ||||||
|         if (bestAction.get()) |         if (bestAction.get()) | ||||||
|             bestAction->prepare(actor); |             bestAction->prepare(actor); | ||||||
| 
 | 
 | ||||||
|         return bestAction; |         return bestAction; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool minusZDist) | ||||||
|  |     { | ||||||
|  |         osg::Vec3f actor1Pos = actor1.getRefData().getPosition().asVec3(); | ||||||
|  |         osg::Vec3f actor2Pos = actor2.getRefData().getPosition().asVec3(); | ||||||
|  | 
 | ||||||
|  |         float dist = (actor1Pos - actor2Pos).length(); | ||||||
|  | 
 | ||||||
|  |         if (minusZDist) | ||||||
|  |             dist -= std::abs(actor1Pos.z() - actor2Pos.z()); | ||||||
|  | 
 | ||||||
|  |         return (dist | ||||||
|  |                 - MWBase::Environment::get().getWorld()->getHalfExtents(actor1).y() | ||||||
|  |                 - MWBase::Environment::get().getWorld()->getHalfExtents(actor2).y()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     float getMaxAttackDistance(const MWWorld::Ptr& actor) | ||||||
|  |     { | ||||||
|  |         const CreatureStats& stats = actor.getClass().getCreatureStats(actor); | ||||||
|  |         const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); | ||||||
|  | 
 | ||||||
|  |         std::string selectedSpellId = stats.getSpells().getSelectedSpell(); | ||||||
|  |         MWWorld::Ptr selectedEnchItem; | ||||||
|  | 
 | ||||||
|  |         MWWorld::Ptr activeWeapon, activeAmmo; | ||||||
|  |         if (actor.getClass().hasInventoryStore(actor)) | ||||||
|  |         { | ||||||
|  |             MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); | ||||||
|  | 
 | ||||||
|  |             MWWorld::ContainerStoreIterator item = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); | ||||||
|  |             if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon) | ||||||
|  |                 activeWeapon = *item; | ||||||
|  | 
 | ||||||
|  |             item = invStore.getSlot(MWWorld::InventoryStore::Slot_Ammunition); | ||||||
|  |             if (item != invStore.end() && item.getType() == MWWorld::ContainerStore::Type_Weapon) | ||||||
|  |                 activeAmmo = *item; | ||||||
|  | 
 | ||||||
|  |             if (invStore.getSelectedEnchantItem() != invStore.end()) | ||||||
|  |                 selectedEnchItem = *invStore.getSelectedEnchantItem(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         float dist = 1.0f; | ||||||
|  |         if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty()) | ||||||
|  |         { | ||||||
|  |             static const float fHandToHandReach = gmst.find("fHandToHandReach")->getFloat(); | ||||||
|  |             dist = fHandToHandReach; | ||||||
|  |         } | ||||||
|  |         else if (stats.getDrawState() == MWMechanics::DrawState_Spell) | ||||||
|  |         { | ||||||
|  |             dist = 1.0f; | ||||||
|  |             if (!selectedSpellId.empty()) | ||||||
|  |             { | ||||||
|  |                 const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(selectedSpellId); | ||||||
|  |                 for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = | ||||||
|  |                      spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) | ||||||
|  |                 { | ||||||
|  |                     if (effectIt->mArea == ESM::RT_Target) | ||||||
|  |                     { | ||||||
|  |                         const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID); | ||||||
|  |                         dist = effect->mData.mSpeed; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (!selectedEnchItem.isEmpty()) | ||||||
|  |             { | ||||||
|  |                 std::string enchId = selectedEnchItem.getClass().getEnchantment(selectedEnchItem); | ||||||
|  |                 if (!enchId.empty()) | ||||||
|  |                 { | ||||||
|  |                     const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchId); | ||||||
|  |                     for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = | ||||||
|  |                          ench->mEffects.mList.begin(); effectIt != ench->mEffects.mList.end(); ++effectIt) | ||||||
|  |                     { | ||||||
|  |                         if (effectIt->mArea == ESM::RT_Target) | ||||||
|  |                         { | ||||||
|  |                             const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID); | ||||||
|  |                             dist = effect->mData.mSpeed; | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); | ||||||
|  |             dist *= std::max(1000.0f, fTargetSpellMaxSpeed); | ||||||
|  |         } | ||||||
|  |         else if (!activeWeapon.isEmpty()) | ||||||
|  |         { | ||||||
|  |             const ESM::Weapon* esmWeap = activeWeapon.get<ESM::Weapon>()->mBase; | ||||||
|  |             if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow) | ||||||
|  |             { | ||||||
|  |                 static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); | ||||||
|  |                 dist = fTargetSpellMaxSpeed; | ||||||
|  |                 if (!activeAmmo.isEmpty()) | ||||||
|  |                 { | ||||||
|  |                     const ESM::Weapon* esmAmmo = activeAmmo.get<ESM::Weapon>()->mBase; | ||||||
|  |                     dist *= esmAmmo->mData.mSpeed; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (esmWeap->mData.mReach > 1) | ||||||
|  |             { | ||||||
|  |                 dist = esmWeap->mData.mReach; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         dist = (dist > 0.f) ? dist : 1.0f; | ||||||
|  | 
 | ||||||
|  |         static const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); | ||||||
|  |         static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->getFloat(); | ||||||
|  | 
 | ||||||
|  |         float combatDistance = fCombatDistance; | ||||||
|  |         if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) | ||||||
|  |             combatDistance *= (fCombatDistanceWerewolfMod + 1.0f); | ||||||
|  | 
 | ||||||
|  |         if (dist < combatDistance) | ||||||
|  |             dist *= combatDistance; | ||||||
|  | 
 | ||||||
|  |         return dist; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) | ||||||
|  |     { | ||||||
|  |         ESM::Position actorPos = actor.getRefData().getPosition(); | ||||||
|  |         ESM::Position enemyPos = enemy.getRefData().getPosition(); | ||||||
|  | 
 | ||||||
|  |         const CreatureStats& enemyStats = enemy.getClass().getCreatureStats(enemy); | ||||||
|  |         if (enemyStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0 | ||||||
|  |                 || enemyStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 0) | ||||||
|  |         { | ||||||
|  |             if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(enemy, actor)) | ||||||
|  |                 return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (actor.getClass().isPureWaterCreature(actor)) | ||||||
|  |         { | ||||||
|  |             if (!MWBase::Environment::get().getWorld()->isWading(enemy)) | ||||||
|  |                 return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         float atDist = getMaxAttackDistance(actor); | ||||||
|  |         if (atDist > getDistanceMinusHalfExtents(actor, enemy) | ||||||
|  |                 && atDist > std::abs(actorPos.pos[2] - enemyPos.pos[2])) | ||||||
|  |         { | ||||||
|  |             if (MWBase::Environment::get().getWorld()->getLOS(actor, enemy)) | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (actor.getClass().isPureFlyingCreature(actor) || actor.getClass().isPureLandCreature(actor)) | ||||||
|  |         { | ||||||
|  |             if (MWBase::Environment::get().getWorld()->isSwimming(enemy)) | ||||||
|  |                 return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (actor.getClass().isBipedal(actor) || !actor.getClass().canFly(actor)) | ||||||
|  |         { | ||||||
|  |             if (enemy.getClass().getCreatureStats(enemy).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0) | ||||||
|  |             { | ||||||
|  |                 float attackDistance = getMaxAttackDistance(actor); | ||||||
|  |                 if ((attackDistance + actorPos.pos[2]) < enemyPos.pos[2]) | ||||||
|  |                 { | ||||||
|  |                     if (enemy.getCell()->isExterior()) | ||||||
|  |                     { | ||||||
|  |                         if (attackDistance < (enemyPos.pos[2] - MWBase::Environment::get().getWorld()->getTerrainHeightAt(enemyPos.asVec3()))) | ||||||
|  |                             return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!actor.getClass().canWalk(actor) && !actor.getClass().isBipedal(actor)) | ||||||
|  |             return true; | ||||||
|  | 
 | ||||||
|  |         if (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0) | ||||||
|  |             return true; | ||||||
|  | 
 | ||||||
|  |         if (MWBase::Environment::get().getWorld()->isSwimming(actor)) | ||||||
|  |             return true; | ||||||
|  | 
 | ||||||
|  |         if (getDistanceMinusHalfExtents(actor, enemy, true) <= 0.0f) | ||||||
|  |             return false; | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) | ||||||
|  |     { | ||||||
|  |         const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); | ||||||
|  | 
 | ||||||
|  |         static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->getFloat(); | ||||||
|  |         static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->getFloat(); | ||||||
|  | 
 | ||||||
|  |         float mult = fAIMagicSpellMult; | ||||||
|  | 
 | ||||||
|  |         for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = | ||||||
|  |              spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) | ||||||
|  |         { | ||||||
|  |             if (effectIt->mArea == ESM::RT_Target) | ||||||
|  |             { | ||||||
|  |                 if (!MWBase::Environment::get().getWorld()->isSwimming(enemy)) | ||||||
|  |                     mult = fAIRangeMagicSpellMult; | ||||||
|  |                 else | ||||||
|  |                     mult = 0.0f; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return MWMechanics::getSpellSuccessChance(spell, actor) * mult; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) | ||||||
|  |     { | ||||||
|  |         const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); | ||||||
|  | 
 | ||||||
|  |         static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat(); | ||||||
|  |         static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat(); | ||||||
|  |         static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat(); | ||||||
|  | 
 | ||||||
|  |         if (weapon.isEmpty()) | ||||||
|  |             return 0.f; | ||||||
|  | 
 | ||||||
|  |         float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f; | ||||||
|  |         float chopMult = fAIMeleeWeaponMult; | ||||||
|  |         float bonusDamage = 0.f; | ||||||
|  | 
 | ||||||
|  |         const ESM::Weapon* esmWeap = weapon.get<ESM::Weapon>()->mBase; | ||||||
|  | 
 | ||||||
|  |         if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow) | ||||||
|  |         { | ||||||
|  |             if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy)) | ||||||
|  |             { | ||||||
|  |                 bonusDamage = ammo.get<ESM::Weapon>()->mBase->mData.mChop[1]; | ||||||
|  |                 chopMult = fAIRangeMeleeWeaponMult; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 chopMult = 0.f; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         float chopRating = (esmWeap->mData.mChop[1] + bonusDamage) * skillMult * chopMult; | ||||||
|  |         float slashRating = esmWeap->mData.mSlash[1] * skillMult * fAIMeleeWeaponMult; | ||||||
|  |         float thrustRating = esmWeap->mData.mThrust[1] * skillMult * fAIMeleeWeaponMult; | ||||||
|  | 
 | ||||||
|  |         return actor.getClass().getArmorRating(actor) * fAIMeleeArmorMult | ||||||
|  |                     + std::max(std::max(chopRating, slashRating), thrustRating); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) | ||||||
|  |     { | ||||||
|  |         const CreatureStats& stats = actor.getClass().getCreatureStats(actor); | ||||||
|  |         const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); | ||||||
|  | 
 | ||||||
|  |         int flee = stats.getAiSetting(CreatureStats::AI_Flee).getModified(); | ||||||
|  |         if (flee >= 100) | ||||||
|  |             return flee; | ||||||
|  | 
 | ||||||
|  |         static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->getFloat(); | ||||||
|  |         static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->getFloat(); | ||||||
|  | 
 | ||||||
|  |         float healthPercentage = (stats.getHealth().getModified() == 0.0f) | ||||||
|  |                                     ? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified(); | ||||||
|  |         float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult; | ||||||
|  | 
 | ||||||
|  |         static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt(); | ||||||
|  | 
 | ||||||
|  |         if (enemy.getClass().isNpc() && enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack) | ||||||
|  |         { | ||||||
|  |             static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt(); | ||||||
|  |             rating = iWereWolfFleeMod; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (rating != 0.0f) | ||||||
|  |             rating += getFightDistanceBias(actor, enemy); | ||||||
|  | 
 | ||||||
|  |         return rating; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating) | ||||||
|  |     { | ||||||
|  |         float fleeRating = vanillaRateFlee(actor, enemy); | ||||||
|  |         if (fleeRating < 100.0f) | ||||||
|  |             fleeRating = 0.0f; | ||||||
|  | 
 | ||||||
|  |         if (fleeRating > antiFleeRating) | ||||||
|  |             return true; | ||||||
|  | 
 | ||||||
|  |         // Run away after summoning a creature if we have nothing to use but fists.
 | ||||||
|  |         if (antiFleeRating == 0.0f && !actor.getClass().getCreatureStats(actor).getSummonedCreatureMap().empty()) | ||||||
|  |             return true; | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -20,6 +20,18 @@ namespace MWMechanics | ||||||
|         virtual float getActionCooldown() { return 0.f; } |         virtual float getActionCooldown() { return 0.f; } | ||||||
|         virtual const ESM::Weapon* getWeapon() const { return NULL; }; |         virtual const ESM::Weapon* getWeapon() const { return NULL; }; | ||||||
|         virtual bool isAttackingOrSpell() const { return true; } |         virtual bool isAttackingOrSpell() const { return true; } | ||||||
|  |         virtual bool isFleeing() const { return false; } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     class ActionFlee : public Action | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         ActionFlee() {} | ||||||
|  |         virtual void prepare(const MWWorld::Ptr& actor) {} | ||||||
|  |         virtual float getCombatRange (bool& isRanged) const { return 0.0f; } | ||||||
|  |         virtual float getActionCooldown() { return 3.0f; } | ||||||
|  |         virtual bool isAttackingOrSpell() const { return false; } | ||||||
|  |         virtual bool isFleeing() const { return true; } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     class ActionSpell : public Action |     class ActionSpell : public Action | ||||||
|  | @ -89,6 +101,15 @@ namespace MWMechanics | ||||||
|     float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); |     float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); | ||||||
| 
 | 
 | ||||||
|     boost::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); |     boost::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); | ||||||
|  | 
 | ||||||
|  |     float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool minusZDist=false); | ||||||
|  |     float getMaxAttackDistance(const MWWorld::Ptr& actor); | ||||||
|  |     bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); | ||||||
|  | 
 | ||||||
|  |     float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); | ||||||
|  |     float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); | ||||||
|  |     float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); | ||||||
|  |     bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -152,7 +152,7 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte | ||||||
| 
 | 
 | ||||||
|         if (dist > 450) |         if (dist > 450) | ||||||
|             actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
 |             actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
 | ||||||
|         else if (dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
 |         else if (dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshold
 | ||||||
|             actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
 |             actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -141,6 +141,79 @@ namespace MWMechanics | ||||||
|         return selectedSpells; |         return selectedSpells; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     std::vector<std::string> autoCalcPlayerSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race) | ||||||
|  |     { | ||||||
|  |         const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); | ||||||
|  | 
 | ||||||
|  |         static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat(); | ||||||
|  | 
 | ||||||
|  |         float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; | ||||||
|  |         bool reachedLimit = false; | ||||||
|  |         const ESM::Spell* weakestSpell = NULL; | ||||||
|  |         int minCost = INT_MAX; | ||||||
|  | 
 | ||||||
|  |         std::vector<std::string> selectedSpells; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         const MWWorld::Store<ESM::Spell> &spells = | ||||||
|  |             esmStore.get<ESM::Spell>(); | ||||||
|  |         for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter) | ||||||
|  |         { | ||||||
|  |             const ESM::Spell* spell = &*iter; | ||||||
|  | 
 | ||||||
|  |             if (spell->mData.mType != ESM::Spell::ST_Spell) | ||||||
|  |                 continue; | ||||||
|  |             if (!(spell->mData.mFlags & ESM::Spell::F_PCStart)) | ||||||
|  |                 continue; | ||||||
|  |             if (reachedLimit && spell->mData.mCost <= minCost) | ||||||
|  |                 continue; | ||||||
|  |             if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) | ||||||
|  |                 continue; | ||||||
|  |             if (baseMagicka < spell->mData.mCost) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat(); | ||||||
|  |             if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             if (!attrSkillCheck(spell, actorSkills, actorAttributes)) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             selectedSpells.push_back(spell->mId); | ||||||
|  | 
 | ||||||
|  |             if (reachedLimit) | ||||||
|  |             { | ||||||
|  |                 std::vector<std::string>::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId); | ||||||
|  |                 if (it != selectedSpells.end()) | ||||||
|  |                     selectedSpells.erase(it); | ||||||
|  | 
 | ||||||
|  |                 minCost = INT_MAX; | ||||||
|  |                 for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) | ||||||
|  |                 { | ||||||
|  |                     const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt); | ||||||
|  |                     if (testSpell->mData.mCost < minCost) | ||||||
|  |                     { | ||||||
|  |                         minCost = testSpell->mData.mCost; | ||||||
|  |                         weakestSpell = testSpell; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (spell->mData.mCost < minCost) | ||||||
|  |                 { | ||||||
|  |                     weakestSpell = spell; | ||||||
|  |                     minCost = weakestSpell->mData.mCost; | ||||||
|  |                 } | ||||||
|  |                 static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt(); | ||||||
|  |                 if (selectedSpells.size() == iAutoPCSpellMax) | ||||||
|  |                     reachedLimit = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return selectedSpells; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes) |     bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes) | ||||||
|     { |     { | ||||||
|         const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList; |         const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList; | ||||||
|  |  | ||||||
|  | @ -16,6 +16,8 @@ namespace MWMechanics | ||||||
| 
 | 
 | ||||||
| std::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); | std::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); | ||||||
| 
 | 
 | ||||||
|  | std::vector<std::string> autoCalcPlayerSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); | ||||||
|  | 
 | ||||||
| // Helpers
 | // Helpers
 | ||||||
| 
 | 
 | ||||||
| bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes); | bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes); | ||||||
|  |  | ||||||
|  | @ -1049,6 +1049,9 @@ bool CharacterController::updateCreatureState() | ||||||
|                 mUpperBodyState = UpperCharState_StartToMinAttack; |                 mUpperBodyState = UpperCharState_StartToMinAttack; | ||||||
| 
 | 
 | ||||||
|                 mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability()); |                 mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability()); | ||||||
|  | 
 | ||||||
|  |                 if (weapType == WeapType_HandToHand) | ||||||
|  |                     playSwishSound(0.0f); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -1370,13 +1373,7 @@ bool CharacterController::updateWeaponState() | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     std::string sound = "SwishM"; |                     playSwishSound(attackStrength); | ||||||
|                     if(attackStrength < 0.5f) |  | ||||||
|                         sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack
 |  | ||||||
|                     else if(attackStrength < 1.0f) |  | ||||||
|                         sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack
 |  | ||||||
|                     else |  | ||||||
|                         sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack
 |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             mAttackStrength = attackStrength; |             mAttackStrength = attackStrength; | ||||||
|  | @ -2271,6 +2268,19 @@ void CharacterController::setHeadTrackTarget(const MWWorld::ConstPtr &target) | ||||||
|     mHeadTrackTarget = target; |     mHeadTrackTarget = target; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void CharacterController::playSwishSound(float attackStrength) | ||||||
|  | { | ||||||
|  |     MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); | ||||||
|  | 
 | ||||||
|  |     std::string sound = "Weapon Swish"; | ||||||
|  |     if(attackStrength < 0.5f) | ||||||
|  |         sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack
 | ||||||
|  |     else if(attackStrength < 1.0f) | ||||||
|  |         sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack
 | ||||||
|  |     else | ||||||
|  |         sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void CharacterController::updateHeadTracking(float duration) | void CharacterController::updateHeadTracking(float duration) | ||||||
| { | { | ||||||
|     const osg::Node* head = mAnimation->getNode("Bip01 Head"); |     const osg::Node* head = mAnimation->getNode("Bip01 Head"); | ||||||
|  |  | ||||||
|  | @ -281,6 +281,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr.
 |     /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr.
 | ||||||
|     void setHeadTrackTarget(const MWWorld::ConstPtr& target); |     void setHeadTrackTarget(const MWWorld::ConstPtr& target); | ||||||
|  | 
 | ||||||
|  |     void playSwishSound(float attackStrength); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|     MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); |     MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); | ||||||
|  |  | ||||||
|  | @ -431,4 +431,19 @@ namespace MWMechanics | ||||||
| 
 | 
 | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2) | ||||||
|  |     { | ||||||
|  |         osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); | ||||||
|  |         osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); | ||||||
|  | 
 | ||||||
|  |         float d = (pos1 - pos2).length(); | ||||||
|  | 
 | ||||||
|  |         static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( | ||||||
|  |                     "iFightDistanceBase")->getInt(); | ||||||
|  |         static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( | ||||||
|  |                     "fFightDistanceMultiplier")->getFloat(); | ||||||
|  | 
 | ||||||
|  |         return (iFightDistanceBase - fFightDistanceMultiplier * d); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,6 +42,7 @@ void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, | ||||||
| /// e.g. If attacker is a fish, is victim in water? Or, if attacker can't swim, is victim on land?
 | /// e.g. If attacker is a fish, is victim in water? Or, if attacker can't swim, is victim on land?
 | ||||||
| bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); | bool isEnvironmentCompatible(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); | ||||||
| 
 | 
 | ||||||
|  | float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -25,25 +25,11 @@ | ||||||
| #include "autocalcspell.hpp" | #include "autocalcspell.hpp" | ||||||
| #include "npcstats.hpp" | #include "npcstats.hpp" | ||||||
| #include "actorutil.hpp" | #include "actorutil.hpp" | ||||||
|  | #include "combat.hpp" | ||||||
| 
 | 
 | ||||||
| namespace | namespace | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2) |  | ||||||
|     { |  | ||||||
|         osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); |  | ||||||
|         osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); |  | ||||||
| 
 |  | ||||||
|         float d = (pos1 - pos2).length(); |  | ||||||
| 
 |  | ||||||
|         static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( |  | ||||||
|                     "iFightDistanceBase")->getInt(); |  | ||||||
|         static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( |  | ||||||
|                     "fFightDistanceMultiplier")->getFloat(); |  | ||||||
| 
 |  | ||||||
|         return (iFightDistanceBase - fFightDistanceMultiplier * d); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     float getFightDispositionBias(float disposition) |     float getFightDispositionBias(float disposition) | ||||||
|     { |     { | ||||||
|         static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( |         static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( | ||||||
|  | @ -212,15 +198,6 @@ namespace MWMechanics | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // F_PCStart spells
 |         // F_PCStart spells
 | ||||||
|         static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat(); |  | ||||||
| 
 |  | ||||||
|         float baseMagicka = fPCbaseMagickaMult * creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase(); |  | ||||||
|         bool reachedLimit = false; |  | ||||||
|         const ESM::Spell* weakestSpell = NULL; |  | ||||||
|         int minCost = INT_MAX; |  | ||||||
| 
 |  | ||||||
|         std::vector<std::string> selectedSpells; |  | ||||||
| 
 |  | ||||||
|         const ESM::Race* race = NULL; |         const ESM::Race* race = NULL; | ||||||
|         if (mRaceSelected) |         if (mRaceSelected) | ||||||
|             race = esmStore.get<ESM::Race>().find(player->mRace); |             race = esmStore.get<ESM::Race>().find(player->mRace); | ||||||
|  | @ -233,61 +210,7 @@ namespace MWMechanics | ||||||
|         for (int i=0; i<ESM::Attribute::Length; ++i) |         for (int i=0; i<ESM::Attribute::Length; ++i) | ||||||
|             attributes[i] = npcStats.getAttribute(i).getBase(); |             attributes[i] = npcStats.getAttribute(i).getBase(); | ||||||
| 
 | 
 | ||||||
|         const MWWorld::Store<ESM::Spell> &spells = |         std::vector<std::string> selectedSpells = autoCalcPlayerSpells(skills, attributes, race); | ||||||
|             esmStore.get<ESM::Spell>(); |  | ||||||
|         for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter) |  | ||||||
|         { |  | ||||||
|             const ESM::Spell* spell = &*iter; |  | ||||||
| 
 |  | ||||||
|             if (spell->mData.mType != ESM::Spell::ST_Spell) |  | ||||||
|                 continue; |  | ||||||
|             if (!(spell->mData.mFlags & ESM::Spell::F_PCStart)) |  | ||||||
|                 continue; |  | ||||||
|             if (reachedLimit && spell->mData.mCost <= minCost) |  | ||||||
|                 continue; |  | ||||||
|             if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) |  | ||||||
|                 continue; |  | ||||||
|             if (baseMagicka < spell->mData.mCost) |  | ||||||
|                 continue; |  | ||||||
| 
 |  | ||||||
|             static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat(); |  | ||||||
|             if (calcAutoCastChance(spell, skills, attributes, -1) < fAutoPCSpellChance) |  | ||||||
|                 continue; |  | ||||||
| 
 |  | ||||||
|             if (!attrSkillCheck(spell, skills, attributes)) |  | ||||||
|                 continue; |  | ||||||
| 
 |  | ||||||
|             selectedSpells.push_back(spell->mId); |  | ||||||
| 
 |  | ||||||
|             if (reachedLimit) |  | ||||||
|             { |  | ||||||
|                 std::vector<std::string>::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId); |  | ||||||
|                 if (it != selectedSpells.end()) |  | ||||||
|                     selectedSpells.erase(it); |  | ||||||
| 
 |  | ||||||
|                 minCost = INT_MAX; |  | ||||||
|                 for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) |  | ||||||
|                 { |  | ||||||
|                     const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt); |  | ||||||
|                     if (testSpell->mData.mCost < minCost) |  | ||||||
|                     { |  | ||||||
|                         minCost = testSpell->mData.mCost; |  | ||||||
|                         weakestSpell = testSpell; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 if (spell->mData.mCost < minCost) |  | ||||||
|                 { |  | ||||||
|                     weakestSpell = spell; |  | ||||||
|                     minCost = weakestSpell->mData.mCost; |  | ||||||
|                 } |  | ||||||
|                 static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt(); |  | ||||||
|                 if (selectedSpells.size() == iAutoPCSpellMax) |  | ||||||
|                     reachedLimit = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         for (std::vector<std::string>::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) |         for (std::vector<std::string>::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) | ||||||
|             creatureStats.getSpells().add(*it); |             creatureStats.getSpells().add(*it); | ||||||
|  | @ -313,7 +236,7 @@ namespace MWMechanics | ||||||
|     : mWatchedTimeToStartDrowning(0), mWatchedStatsEmpty (true), mUpdatePlayer (true), mClassSelected (false), |     : mWatchedTimeToStartDrowning(0), mWatchedStatsEmpty (true), mUpdatePlayer (true), mClassSelected (false), | ||||||
|       mRaceSelected (false), mAI(true) |       mRaceSelected (false), mAI(true) | ||||||
|     { |     { | ||||||
|         //buildPlayer no longer here, needs to be done explicitely after all subsystems are up and running
 |         //buildPlayer no longer here, needs to be done explicitly after all subsystems are up and running
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void MechanicsManager::add(const MWWorld::Ptr& ptr) |     void MechanicsManager::add(const MWWorld::Ptr& ptr) | ||||||
|  | @ -1608,27 +1531,23 @@ namespace MWMechanics | ||||||
|                 player->restoreSkillsAttributes(); |                 player->restoreSkillsAttributes(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Equipped items other than WerewolfRobe may reference bones that do not even
 |  | ||||||
|         // exist with the werewolf object root, so make sure to unequip all items
 |  | ||||||
|         // *before* we become a werewolf.
 |  | ||||||
|         MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); |  | ||||||
|         invStore.unequipAll(actor); |  | ||||||
| 
 |  | ||||||
|         // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one.
 |         // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one.
 | ||||||
|         if (npcStats.getDrawState() == MWMechanics::DrawState_Spell) |         if (npcStats.getDrawState() == MWMechanics::DrawState_Spell) | ||||||
|             npcStats.setDrawState(MWMechanics::DrawState_Nothing); |             npcStats.setDrawState(MWMechanics::DrawState_Nothing); | ||||||
| 
 | 
 | ||||||
|         npcStats.setWerewolf(werewolf); |         npcStats.setWerewolf(werewolf); | ||||||
| 
 | 
 | ||||||
|         if(werewolf) |  | ||||||
|         { |  | ||||||
|         MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor); |         MWWorld::InventoryStore &inv = actor.getClass().getInventoryStore(actor); | ||||||
| 
 | 
 | ||||||
|  |         if(werewolf) | ||||||
|  |         { | ||||||
|  |             inv.unequipAll(actor); | ||||||
|             inv.equip(MWWorld::InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); |             inv.equip(MWWorld::InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); |             inv.unequipSlot(MWWorld::InventoryStore::Slot_Robe, actor); | ||||||
|  |             inv.ContainerStore::remove("werewolfrobe", 1, actor); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(actor == player->getPlayer()) |         if(actor == player->getPlayer()) | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ namespace MWMechanics | ||||||
|     bool proximityToDoor(const MWWorld::Ptr& actor, |     bool proximityToDoor(const MWWorld::Ptr& actor, | ||||||
|                          float minSqr = MIN_DIST_TO_DOOR_SQUARED); |                          float minSqr = MIN_DIST_TO_DOOR_SQUARED); | ||||||
| 
 | 
 | ||||||
|     /// Returns door pointer within range. No guarentee is given as too which one
 |     /// Returns door pointer within range. No guarantee is given as to which one
 | ||||||
|     /** \return Pointer to the door, or NULL if none exists **/ |     /** \return Pointer to the door, or NULL if none exists **/ | ||||||
|     MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, |     MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, | ||||||
|                          float minSqr = MIN_DIST_TO_DOOR_SQUARED); |                          float minSqr = MIN_DIST_TO_DOOR_SQUARED); | ||||||
|  |  | ||||||
|  | @ -223,7 +223,7 @@ namespace MWMechanics | ||||||
|         return 1 - resistance / 100.f; |         return 1 - resistance / 100.f; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure.
 |     /// Check if the given effect can be applied to the target. If \a castByPlayer, emits a message box on failure.
 | ||||||
|     bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) |     bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer) | ||||||
|     { |     { | ||||||
|         switch (effectId) |         switch (effectId) | ||||||
|  | @ -491,7 +491,7 @@ namespace MWMechanics | ||||||
|                         appliedLastingEffects.push_back(effect); |                         appliedLastingEffects.push_back(effect); | ||||||
| 
 | 
 | ||||||
|                         // For absorb effects, also apply the effect to the caster - but with a negative
 |                         // For absorb effects, also apply the effect to the caster - but with a negative
 | ||||||
|                         // magnitude, since we're transfering stats from the target to the caster
 |                         // magnitude, since we're transferring stats from the target to the caster
 | ||||||
|                         if (!caster.isEmpty() && caster.getClass().isActor()) |                         if (!caster.isEmpty() && caster.getClass().isActor()) | ||||||
|                         { |                         { | ||||||
|                             for (int i=0; i<5; ++i) |                             for (int i=0; i<5; ++i) | ||||||
|  | @ -913,7 +913,7 @@ namespace MWMechanics | ||||||
| 
 | 
 | ||||||
|             MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); |             MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); | ||||||
| 
 | 
 | ||||||
|             if (mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx
 |             if (animation && mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx even if they are disabled (animation == NULL)
 | ||||||
|             { |             { | ||||||
|                 const ESM::Static* castStatic; |                 const ESM::Static* castStatic; | ||||||
| 
 | 
 | ||||||
|  | @ -927,7 +927,7 @@ namespace MWMechanics | ||||||
|                 animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture); |                 animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (!mCaster.getClass().isActor()) |             if (animation && !mCaster.getClass().isActor()) | ||||||
|                 animation->addSpellCastGlow(effect); |                 animation->addSpellCastGlow(effect); | ||||||
| 
 | 
 | ||||||
|             static const std::string schools[] = { |             static const std::string schools[] = { | ||||||
|  | @ -980,8 +980,10 @@ namespace MWMechanics | ||||||
|                 if (charge == 0) |                 if (charge == 0) | ||||||
|                     return false; |                     return false; | ||||||
| 
 | 
 | ||||||
|                 // FIXME: charge should be a float, not int so that damage < 1 per frame can be applied.
 |                 // Store remainder of disintegrate amount (automatically subtracted if > 1)
 | ||||||
|                 // This was also a bug in the original engine.
 |                 item->getCellRef().applyChargeRemainderToBeSubtracted(disintegrate - std::floor(disintegrate)); | ||||||
|  | 
 | ||||||
|  |                 charge = item->getClass().getItemHealth(*item); | ||||||
|                 charge -= |                 charge -= | ||||||
|                         std::min(static_cast<int>(disintegrate), |                         std::min(static_cast<int>(disintegrate), | ||||||
|                                  charge); |                                  charge); | ||||||
|  | @ -1009,10 +1011,10 @@ namespace MWMechanics | ||||||
|         creatureStats.setDynamic(index, stat); |         creatureStats.setDynamic(index, stat); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude) |     bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey &effectKey, float magnitude) | ||||||
|     { |     { | ||||||
|         if (magnitude == 0.f) |         if (magnitude == 0.f) | ||||||
|             return; |             return false; | ||||||
| 
 | 
 | ||||||
|         bool receivedMagicDamage = false; |         bool receivedMagicDamage = false; | ||||||
| 
 | 
 | ||||||
|  | @ -1144,10 +1146,13 @@ namespace MWMechanics | ||||||
|         case ESM::MagicEffect::RemoveCurse: |         case ESM::MagicEffect::RemoveCurse: | ||||||
|             actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); |             actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); | ||||||
|             break; |             break; | ||||||
|  |         default: | ||||||
|  |             return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (receivedMagicDamage && actor == getPlayer()) |         if (receivedMagicDamage && actor == getPlayer()) | ||||||
|             MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); |             MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -59,9 +59,13 @@ namespace MWMechanics | ||||||
|     float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, |     float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, | ||||||
|                               const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); |                               const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); | ||||||
| 
 | 
 | ||||||
|  |     bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); | ||||||
|  | 
 | ||||||
|     int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); |     int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); | ||||||
| 
 | 
 | ||||||
|     void effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); |     /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed
 | ||||||
|  |     /// @return Was the effect a tickable effect with a magnitude?
 | ||||||
|  |     bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); | ||||||
| 
 | 
 | ||||||
|     class CastSpell |     class CastSpell | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -69,7 +69,14 @@ namespace MWPhysics | ||||||
|             return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f))); |             return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f))); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         static bool stepMove(const btCollisionObject *colobj, osg::Vec3f &position, |         enum StepMoveResult | ||||||
|  |         { | ||||||
|  |             Result_Blocked, // unable to move over obstacle
 | ||||||
|  |             Result_MaxSlope, // unable to end movement on this slope
 | ||||||
|  |             Result_Success | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         static StepMoveResult stepMove(const btCollisionObject *colobj, osg::Vec3f &position, | ||||||
|                              const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld) |                              const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld) | ||||||
|         { |         { | ||||||
|             /*
 |             /*
 | ||||||
|  | @ -120,7 +127,7 @@ namespace MWPhysics | ||||||
| 
 | 
 | ||||||
|             stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld); |             stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld); | ||||||
|             if(stepper.mFraction < std::numeric_limits<float>::epsilon()) |             if(stepper.mFraction < std::numeric_limits<float>::epsilon()) | ||||||
|                 return false; // didn't even move the smallest representable amount
 |                 return Result_Blocked; // didn't even move the smallest representable amount
 | ||||||
|                               // (TODO: shouldn't this be larger? Why bother with such a small amount?)
 |                               // (TODO: shouldn't this be larger? Why bother with such a small amount?)
 | ||||||
| 
 | 
 | ||||||
|             /*
 |             /*
 | ||||||
|  | @ -138,7 +145,7 @@ namespace MWPhysics | ||||||
|              */ |              */ | ||||||
|             tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld); |             tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld); | ||||||
|             if(tracer.mFraction < std::numeric_limits<float>::epsilon()) |             if(tracer.mFraction < std::numeric_limits<float>::epsilon()) | ||||||
|                 return false; // didn't even move the smallest representable amount
 |                 return Result_Blocked; // didn't even move the smallest representable amount
 | ||||||
| 
 | 
 | ||||||
|             /*
 |             /*
 | ||||||
|              * Try moving back down sStepSizeDown using stepper. |              * Try moving back down sStepSizeDown using stepper. | ||||||
|  | @ -156,22 +163,22 @@ namespace MWPhysics | ||||||
|              *    ============================================== |              *    ============================================== | ||||||
|              */ |              */ | ||||||
|             stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); |             stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); | ||||||
|             if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) |             if (getSlope(stepper.mPlaneNormal) > sMaxSlope) | ||||||
|  |                 return Result_MaxSlope; | ||||||
|  |             if(stepper.mFraction < 1.0f) | ||||||
|             { |             { | ||||||
|                 // don't allow stepping up other actors
 |                 // don't allow stepping up other actors
 | ||||||
|                 if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) |                 if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) | ||||||
|                     return false; |                     return Result_Blocked; | ||||||
|                 // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
 |                 // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
 | ||||||
|                 // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing
 |                 // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing
 | ||||||
|                 // NOTE: caller's variables 'position' & 'remainingTime' are modified here
 |                 // NOTE: caller's variables 'position' & 'remainingTime' are modified here
 | ||||||
|                 position = stepper.mEndPos; |                 position = stepper.mEndPos; | ||||||
|                 remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance
 |                 remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance
 | ||||||
|                 return true; |                 return Result_Success; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // moved between 0 and just under sStepSize distance but slope was too great,
 |             return Result_Blocked; | ||||||
|             // or moved full sStepSize distance (FIXME: is this a bug?)
 |  | ||||||
|             return false; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -361,14 +368,15 @@ namespace MWPhysics | ||||||
|                 osg::Vec3f oldPosition = newPosition; |                 osg::Vec3f oldPosition = newPosition; | ||||||
|                 // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over)
 |                 // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over)
 | ||||||
|                 // NOTE: stepMove modifies newPosition if successful
 |                 // NOTE: stepMove modifies newPosition if successful
 | ||||||
|                 bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); |                 const float minStep = 10.f; | ||||||
|                 if (!result) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent
 |                 StepMoveResult result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); | ||||||
|  |                 if (result == Result_MaxSlope && (velocity*remainingTime).length() < minStep) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent
 | ||||||
|                 { |                 { | ||||||
|                     osg::Vec3f normalizedVelocity = velocity; |                     osg::Vec3f normalizedVelocity = velocity; | ||||||
|                     normalizedVelocity.normalize(); |                     normalizedVelocity.normalize(); | ||||||
|                     result = stepMove(colobj, newPosition, normalizedVelocity*10.f, remainingTime, collisionWorld); |                     result = stepMove(colobj, newPosition, normalizedVelocity*minStep, remainingTime, collisionWorld); | ||||||
|                 } |                 } | ||||||
|                 if(result) |                 if(result == Result_Success) | ||||||
|                 { |                 { | ||||||
|                     // don't let pure water creatures move out of water after stepMove
 |                     // don't let pure water creatures move out of water after stepMove
 | ||||||
|                     if (ptr.getClass().isPureWaterCreature(ptr) |                     if (ptr.getClass().isPureWaterCreature(ptr) | ||||||
|  | @ -450,8 +458,8 @@ namespace MWPhysics | ||||||
|                 if (inertia.z() < 0) |                 if (inertia.z() < 0) | ||||||
|                     inertia.z() *= slowFall; |                     inertia.z() *= slowFall; | ||||||
|                 if (slowFall < 1.f) { |                 if (slowFall < 1.f) { | ||||||
|                     inertia.x() = 0; |                     inertia.x() *= slowFall; | ||||||
|                     inertia.y() = 0; |                     inertia.y() *= slowFall; | ||||||
|                 } |                 } | ||||||
|                 physicActor->setInertialForce(inertia); |                 physicActor->setInertialForce(inertia); | ||||||
|             } |             } | ||||||
|  | @ -985,6 +993,18 @@ namespace MWPhysics | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel) | ||||||
|  |     { | ||||||
|  |         const Actor* physicActor = getActor(actor); | ||||||
|  |         const float halfZ = physicActor->getHalfExtents().z(); | ||||||
|  |         const osg::Vec3f actorPosition = physicActor->getPosition(); | ||||||
|  |         const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ); | ||||||
|  |         const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ); | ||||||
|  |         ActorTracer tracer; | ||||||
|  |         tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld); | ||||||
|  |         return (tracer.mFraction >= 1.0f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const |     osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const | ||||||
|     { |     { | ||||||
|         const Actor* physactor = getActor(actor); |         const Actor* physactor = getActor(actor); | ||||||
|  | @ -1309,25 +1329,29 @@ namespace MWPhysics | ||||||
|         PtrVelocityList::iterator iter = mMovementQueue.begin(); |         PtrVelocityList::iterator iter = mMovementQueue.begin(); | ||||||
|         for(;iter != mMovementQueue.end();++iter) |         for(;iter != mMovementQueue.end();++iter) | ||||||
|         { |         { | ||||||
|  |             ActorMap::iterator foundActor = mActors.find(iter->first); | ||||||
|  |             if (foundActor == mActors.end()) // actor was already removed from the scene
 | ||||||
|  |                 continue; | ||||||
|  |             Actor* physicActor = foundActor->second; | ||||||
|  | 
 | ||||||
|             float waterlevel = -std::numeric_limits<float>::max(); |             float waterlevel = -std::numeric_limits<float>::max(); | ||||||
|             const MWWorld::CellStore *cell = iter->first.getCell(); |             const MWWorld::CellStore *cell = iter->first.getCell(); | ||||||
|             if(cell->getCell()->hasWater()) |             if(cell->getCell()->hasWater()) | ||||||
|                 waterlevel = cell->getWaterLevel(); |                 waterlevel = cell->getWaterLevel(); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|             const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); |             const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); | ||||||
| 
 | 
 | ||||||
|             bool waterCollision = false; |             bool waterCollision = false; | ||||||
|             if (effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() |             if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude()) | ||||||
|                     && cell->getCell()->hasWater() |             { | ||||||
|                     && !world->isUnderwater(iter->first.getCell(), |                 if (!world->isUnderwater(iter->first.getCell(), osg::Vec3f(iter->first.getRefData().getPosition().asVec3()))) | ||||||
|                                            osg::Vec3f(iter->first.getRefData().getPosition().asVec3()))) |  | ||||||
|                     waterCollision = true; |                     waterCollision = true; | ||||||
| 
 |                 else if (physicActor->getCollisionMode() && canMoveToWaterSurface(iter->first, waterlevel)) | ||||||
|             ActorMap::iterator foundActor = mActors.find(iter->first); |                 { | ||||||
|             if (foundActor == mActors.end()) // actor was already removed from the scene
 |                     const osg::Vec3f actorPosition = physicActor->getPosition(); | ||||||
|                 continue; |                     physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel));             | ||||||
|             Actor* physicActor = foundActor->second; |                 } | ||||||
|  |             } | ||||||
|             physicActor->setCanWaterWalk(waterCollision); |             physicActor->setCanWaterWalk(waterCollision); | ||||||
| 
 | 
 | ||||||
|             // Slow fall reduces fall speed by a factor of (effect magnitude / 200)
 |             // Slow fall reduces fall speed by a factor of (effect magnitude / 200)
 | ||||||
|  |  | ||||||
|  | @ -123,6 +123,8 @@ namespace MWPhysics | ||||||
| 
 | 
 | ||||||
|             bool isOnGround (const MWWorld::Ptr& actor); |             bool isOnGround (const MWWorld::Ptr& actor); | ||||||
| 
 | 
 | ||||||
|  |             bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel); | ||||||
|  | 
 | ||||||
|             /// Get physical half extents (scaled) of the given actor.
 |             /// Get physical half extents (scaled) of the given actor.
 | ||||||
|             osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const; |             osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -686,9 +686,9 @@ namespace MWRender | ||||||
|                 state.mAutoDisable = autodisable; |                 state.mAutoDisable = autodisable; | ||||||
|                 mStates[groupname] = state; |                 mStates[groupname] = state; | ||||||
| 
 | 
 | ||||||
|                 NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); |  | ||||||
|                 if (state.mPlaying) |                 if (state.mPlaying) | ||||||
|                 { |                 { | ||||||
|  |                     NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.getTime())); | ||||||
|                     while(textkey != textkeys.end() && textkey->first <= state.getTime()) |                     while(textkey != textkeys.end() && textkey->first <= state.getTime()) | ||||||
|                     { |                     { | ||||||
|                         handleTextKey(state, groupname, textkey, textkeys); |                         handleTextKey(state, groupname, textkey, textkeys); | ||||||
|  | @ -976,14 +976,14 @@ namespace MWRender | ||||||
| 
 | 
 | ||||||
|             while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend()) |             while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend()) | ||||||
|             { |             { | ||||||
|                 const NifOsg::TextKeyMap &keys = (*animiter)->getTextKeys(); |                 const NifOsg::TextKeyMap &keys2 = (*animiter)->getTextKeys(); | ||||||
| 
 | 
 | ||||||
|                 const AnimSource::ControllerMap& ctrls2 = (*animiter)->mControllerMap[0]; |                 const AnimSource::ControllerMap& ctrls2 = (*animiter)->mControllerMap[0]; | ||||||
|                 for (AnimSource::ControllerMap::const_iterator it = ctrls2.begin(); it != ctrls2.end(); ++it) |                 for (AnimSource::ControllerMap::const_iterator it = ctrls2.begin(); it != ctrls2.end(); ++it) | ||||||
|                 { |                 { | ||||||
|                     if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName())) |                     if (Misc::StringUtils::ciEqual(it->first, mAccumRoot->getName())) | ||||||
|                     { |                     { | ||||||
|                         velocity = calcAnimVelocity(keys, it->second, mAccumulate, groupname); |                         velocity = calcAnimVelocity(keys2, it->second, mAccumulate, groupname); | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -302,7 +302,7 @@ protected: | ||||||
| 
 | 
 | ||||||
|     /** Sets the root model of the object.
 |     /** Sets the root model of the object.
 | ||||||
|      * |      * | ||||||
|      * Note that you must make sure all animation sources are cleared before reseting the object |      * Note that you must make sure all animation sources are cleared before resetting the object | ||||||
|      * root. All nodes previously retrieved with getNode will also become invalidated. |      * root. All nodes previously retrieved with getNode will also become invalidated. | ||||||
|      * @param forceskeleton Wrap the object root in a Skeleton, even if it contains no skinned parts. Use this if you intend to add skinned parts manually. |      * @param forceskeleton Wrap the object root in a Skeleton, even if it contains no skinned parts. Use this if you intend to add skinned parts manually. | ||||||
|      * @param baseonly If true, then any meshes or particle systems in the model are ignored |      * @param baseonly If true, then any meshes or particle systems in the model are ignored | ||||||
|  |  | ||||||
|  | @ -221,10 +221,10 @@ namespace MWRender | ||||||
|             groupname = "inventoryhandtohand"; |             groupname = "inventoryhandtohand"; | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             const std::string &type = iter->getTypeName(); |             const std::string &typeName = iter->getTypeName(); | ||||||
|             if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) |             if(typeName == typeid(ESM::Lockpick).name() || typeName == typeid(ESM::Probe).name()) | ||||||
|                 groupname = "inventoryweapononehand"; |                 groupname = "inventoryweapononehand"; | ||||||
|             else if(type == typeid(ESM::Weapon).name()) |             else if(typeName == typeid(ESM::Weapon).name()) | ||||||
|             { |             { | ||||||
|                 MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>(); |                 MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -403,6 +403,9 @@ void NpcAnimation::rebuild() | ||||||
| { | { | ||||||
|     updateNpcBase(); |     updateNpcBase(); | ||||||
| 
 | 
 | ||||||
|  |     if (mAlpha != 1.f) | ||||||
|  |         mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); | ||||||
|  | 
 | ||||||
|     MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); |     MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -503,10 +506,10 @@ void NpcAnimation::updateNpcBase() | ||||||
| 
 | 
 | ||||||
|         if(!isWerewolf) |         if(!isWerewolf) | ||||||
|         { |         { | ||||||
|             if(Misc::StringUtils::lowerCase(mNpc->mRace).find("argonian") != std::string::npos) |  | ||||||
|                 addAnimSource("meshes\\xargonian_swimkna.nif"); |  | ||||||
|             if(mNpc->mModel.length() > 0) |             if(mNpc->mModel.length() > 0) | ||||||
|                 addAnimSource(Misc::ResourceHelpers::correctActorModelPath("meshes\\" + mNpc->mModel, mResourceSystem->getVFS())); |                 addAnimSource(Misc::ResourceHelpers::correctActorModelPath("meshes\\" + mNpc->mModel, mResourceSystem->getVFS())); | ||||||
|  |             if(Misc::StringUtils::lowerCase(mNpc->mRace).find("argonian") != std::string::npos) | ||||||
|  |                 addAnimSource("meshes\\xargonian_swimkna.nif"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|  |  | ||||||
|  | @ -458,9 +458,9 @@ namespace MWRender | ||||||
|         { |         { | ||||||
|             mEffectManager->update(dt); |             mEffectManager->update(dt); | ||||||
|             mSky->update(dt); |             mSky->update(dt); | ||||||
|  |             mWater->update(dt); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         mWater->update(dt); |  | ||||||
|         mCamera->update(dt, paused); |         mCamera->update(dt, paused); | ||||||
| 
 | 
 | ||||||
|         osg::Vec3f focal, cameraPos; |         osg::Vec3f focal, cameraPos; | ||||||
|  |  | ||||||
|  | @ -39,11 +39,11 @@ namespace | ||||||
|         { |         { | ||||||
|             std::ostringstream texname; |             std::ostringstream texname; | ||||||
|             texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds"; |             texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds"; | ||||||
|             osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(resourceSystem->getImageManager()->getImage(texname.str()))); |             osg::ref_ptr<osg::Texture2D> tex2 (new osg::Texture2D(resourceSystem->getImageManager()->getImage(texname.str()))); | ||||||
|             tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); |             tex2->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); | ||||||
|             tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); |             tex2->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); | ||||||
|             resourceSystem->getSceneManager()->applyFilterSettings(tex); |             resourceSystem->getSceneManager()->applyFilterSettings(tex2); | ||||||
|             textures.push_back(tex); |             textures.push_back(tex2); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures)); |         osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures)); | ||||||
|  | @ -117,6 +117,7 @@ RippleSimulation::~RippleSimulation() | ||||||
| 
 | 
 | ||||||
| void RippleSimulation::update(float dt) | void RippleSimulation::update(float dt) | ||||||
| { | { | ||||||
|  |     const MWBase::World* world = MWBase::Environment::get().getWorld(); | ||||||
|     for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it) |     for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it) | ||||||
|     { |     { | ||||||
|         if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr()) |         if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr()) | ||||||
|  | @ -128,10 +129,8 @@ void RippleSimulation::update(float dt) | ||||||
| 
 | 
 | ||||||
|         osg::Vec3f currentPos (it->mPtr.getRefData().getPosition().asVec3()); |         osg::Vec3f currentPos (it->mPtr.getRefData().getPosition().asVec3()); | ||||||
| 
 | 
 | ||||||
|         if ( (currentPos - it->mLastEmitPosition).length() > 10 |         bool shouldEmit = ( world->isUnderwater (it->mPtr.getCell(), it->mPtr.getRefData().getPosition().asVec3()) && !world->isSubmerged(it->mPtr) ) || world->isWalkingOnWater(it->mPtr); | ||||||
|              // Only emit when close to the water surface, not above it and not too deep in the water
 |         if ( shouldEmit && (currentPos - it->mLastEmitPosition).length() > 10 ) | ||||||
|             && MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(), it->mPtr.getRefData().getPosition().asVec3()) |  | ||||||
|              && !MWBase::Environment::get().getWorld()->isSubmerged(it->mPtr)) |  | ||||||
|         { |         { | ||||||
|             it->mLastEmitPosition = currentPos; |             it->mLastEmitPosition = currentPos; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1329,7 +1329,7 @@ void SkyManager::createRain() | ||||||
|     mRainParticleSystem = new osgParticle::ParticleSystem; |     mRainParticleSystem = new osgParticle::ParticleSystem; | ||||||
|     mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED); |     mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED); | ||||||
|     mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0)); |     mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0)); | ||||||
|     mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,-1)); |     mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,1)); | ||||||
| 
 | 
 | ||||||
|     osg::ref_ptr<osg::StateSet> stateset (mRainParticleSystem->getOrCreateStateSet()); |     osg::ref_ptr<osg::StateSet> stateset (mRainParticleSystem->getOrCreateStateSet()); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -238,15 +238,15 @@ namespace MWScript | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     char type =  declarations.getType (iter->first); |                     char type =  declarations.getType (iter->first); | ||||||
|                     char index = declarations.getIndex (iter->first); |                     int index2 = declarations.getIndex (iter->first); | ||||||
| 
 | 
 | ||||||
|                     try |                     try | ||||||
|                     { |                     { | ||||||
|                         switch (type) |                         switch (type) | ||||||
|                         { |                         { | ||||||
|                             case 's': mShorts.at (index) = iter->second.getInteger(); break; |                             case 's': mShorts.at (index2) = iter->second.getInteger(); break; | ||||||
|                             case 'l': mLongs.at (index) = iter->second.getInteger(); break; |                             case 'l': mLongs.at (index2) = iter->second.getInteger(); break; | ||||||
|                             case 'f': mFloats.at (index) = iter->second.getFloat(); break; |                             case 'f': mFloats.at (index2) = iter->second.getFloat(); break; | ||||||
| 
 | 
 | ||||||
|                             // silently ignore locals that don't exist anymore
 |                             // silently ignore locals that don't exist anymore
 | ||||||
|                         } |                         } | ||||||
|  |  | ||||||
|  | @ -34,8 +34,9 @@ | ||||||
| 
 | 
 | ||||||
| namespace MWSound | namespace MWSound | ||||||
| { | { | ||||||
|     SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound) |     SoundManager::SoundManager(const VFS::Manager* vfs, const std::map<std::string,std::string>& fallbackMap, bool useSound) | ||||||
|         : mVFS(vfs) |         : mVFS(vfs) | ||||||
|  |         , mFallback(fallbackMap) | ||||||
|         , mOutput(new DEFAULT_OUTPUT(*this)) |         , mOutput(new DEFAULT_OUTPUT(*this)) | ||||||
|         , mMasterVolume(1.0f) |         , mMasterVolume(1.0f) | ||||||
|         , mSFXVolume(1.0f) |         , mSFXVolume(1.0f) | ||||||
|  | @ -61,6 +62,13 @@ namespace MWSound | ||||||
|         mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); |         mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); | ||||||
|         mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f); |         mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f); | ||||||
| 
 | 
 | ||||||
|  |         mNearWaterRadius = mFallback.getFallbackInt("Water_NearWaterRadius"); | ||||||
|  |         mNearWaterPoints = mFallback.getFallbackInt("Water_NearWaterPoints"); | ||||||
|  |         mNearWaterIndoorTolerance = mFallback.getFallbackFloat("Water_NearWaterIndoorTolerance"); | ||||||
|  |         mNearWaterOutdoorTolerance = mFallback.getFallbackFloat("Water_NearWaterOutdoorTolerance"); | ||||||
|  |         mNearWaterIndoorID = mFallback.getFallbackString("Water_NearWaterIndoorID"); | ||||||
|  |         mNearWaterOutdoorID = mFallback.getFallbackString("Water_NearWaterOutdoorID"); | ||||||
|  | 
 | ||||||
|         mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1); |         mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1); | ||||||
|         mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1); |         mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1); | ||||||
|         mBufferCacheMax *= 1024*1024; |         mBufferCacheMax *= 1024*1024; | ||||||
|  | @ -796,6 +804,96 @@ namespace MWSound | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void SoundManager::updateWaterSound(float /*duration*/) | ||||||
|  |     { | ||||||
|  |         MWBase::World* world = MWBase::Environment::get().getWorld(); | ||||||
|  |         const MWWorld::ConstPtr player = world->getPlayerPtr(); | ||||||
|  |         osg::Vec3f pos = player.getRefData().getPosition().asVec3(); | ||||||
|  | 
 | ||||||
|  |         float volume = 0.0f; | ||||||
|  |         const std::string& soundId = player.getCell()->isExterior() ? mNearWaterOutdoorID : mNearWaterIndoorID; | ||||||
|  | 
 | ||||||
|  |         if (!mListenerUnderwater) | ||||||
|  |         { | ||||||
|  |             if (player.getCell()->getCell()->hasWater()) | ||||||
|  |             { | ||||||
|  |                 float dist = std::abs(player.getCell()->getWaterLevel() - pos.z()); | ||||||
|  | 
 | ||||||
|  |                 if (player.getCell()->isExterior() && dist < mNearWaterOutdoorTolerance) | ||||||
|  |                 { | ||||||
|  |                     volume = (mNearWaterOutdoorTolerance - dist) / mNearWaterOutdoorTolerance; | ||||||
|  | 
 | ||||||
|  |                     if (mNearWaterPoints > 1) | ||||||
|  |                     { | ||||||
|  |                         int underwaterPoints = 0; | ||||||
|  | 
 | ||||||
|  |                         float step = mNearWaterRadius * 2.0f / (mNearWaterPoints - 1); | ||||||
|  | 
 | ||||||
|  |                         for (int x = 0; x < mNearWaterPoints; x++) | ||||||
|  |                         { | ||||||
|  |                             for (int y = 0; y < mNearWaterPoints; y++) | ||||||
|  |                             { | ||||||
|  |                                 float height = world->getTerrainHeightAt( | ||||||
|  |                                             osg::Vec3f(pos.x() - mNearWaterRadius + x*step, pos.y() - mNearWaterRadius + y*step, 0.0f)); | ||||||
|  | 
 | ||||||
|  |                                 if (height < 0) | ||||||
|  |                                     underwaterPoints++; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         volume *= underwaterPoints * 2.0f / (mNearWaterPoints*mNearWaterPoints); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else if (!player.getCell()->isExterior() && dist < mNearWaterIndoorTolerance) | ||||||
|  |                 { | ||||||
|  |                     volume = (mNearWaterIndoorTolerance - dist) / mNearWaterIndoorTolerance; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             volume = 1.0f; | ||||||
|  | 
 | ||||||
|  |         volume = std::min(volume, 1.0f); | ||||||
|  | 
 | ||||||
|  |         if (mNearWaterSound) | ||||||
|  |         { | ||||||
|  |             if (volume == 0.0f) | ||||||
|  |             { | ||||||
|  |                 mOutput->finishSound(mNearWaterSound); | ||||||
|  |                 mNearWaterSound.reset(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 bool soundIdChanged = false; | ||||||
|  | 
 | ||||||
|  |                 Sound_Buffer* sfx = lookupSound(Misc::StringUtils::lowerCase(soundId)); | ||||||
|  | 
 | ||||||
|  |                 for (SoundMap::const_iterator snditer = mActiveSounds.begin(); snditer != mActiveSounds.end(); ++snditer) | ||||||
|  |                 { | ||||||
|  |                     for (SoundBufferRefPairList::const_iterator pairiter = snditer->second.begin(); pairiter != snditer->second.end(); ++pairiter) | ||||||
|  |                     { | ||||||
|  |                         if (pairiter->first == mNearWaterSound) | ||||||
|  |                         { | ||||||
|  |                             if (pairiter->second != sfx) | ||||||
|  |                                 soundIdChanged = true; | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (soundIdChanged) | ||||||
|  |                 { | ||||||
|  |                     mOutput->finishSound(mNearWaterSound); | ||||||
|  |                     mNearWaterSound = playSound(soundId, volume, 1.0f, Play_TypeSfx, Play_Loop); | ||||||
|  |                 } | ||||||
|  |                 else if (sfx) | ||||||
|  |                     mNearWaterSound->setVolume(volume * sfx->mVolume); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else if (volume > 0.0f) | ||||||
|  |             mNearWaterSound = playSound(soundId, volume, 1.0f, Play_TypeSfx, Play_Loop); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void SoundManager::updateSounds(float duration) |     void SoundManager::updateSounds(float duration) | ||||||
|     { |     { | ||||||
|         static float timePassed = 0.0; |         static float timePassed = 0.0; | ||||||
|  | @ -941,6 +1039,7 @@ namespace MWSound | ||||||
|         { |         { | ||||||
|             updateSounds(duration); |             updateSounds(duration); | ||||||
|             updateRegionSound(duration); |             updateRegionSound(duration); | ||||||
|  |             updateWaterSound(duration); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1105,6 +1204,7 @@ namespace MWSound | ||||||
|             mOutput->finishStream(*trkiter); |             mOutput->finishStream(*trkiter); | ||||||
|         mActiveTracks.clear(); |         mActiveTracks.clear(); | ||||||
|         mUnderwaterSound.reset(); |         mUnderwaterSound.reset(); | ||||||
|  |         mNearWaterSound.reset(); | ||||||
|         stopMusic(); |         stopMusic(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,8 @@ | ||||||
| 
 | 
 | ||||||
| #include <components/settings/settings.hpp> | #include <components/settings/settings.hpp> | ||||||
| 
 | 
 | ||||||
|  | #include <components/fallback/fallback.hpp> | ||||||
|  | 
 | ||||||
| #include "../mwbase/soundmanager.hpp" | #include "../mwbase/soundmanager.hpp" | ||||||
| 
 | 
 | ||||||
| namespace VFS | namespace VFS | ||||||
|  | @ -44,6 +46,8 @@ namespace MWSound | ||||||
|     { |     { | ||||||
|         const VFS::Manager* mVFS; |         const VFS::Manager* mVFS; | ||||||
| 
 | 
 | ||||||
|  |         Fallback::Map mFallback; | ||||||
|  | 
 | ||||||
|         std::auto_ptr<Sound_Output> mOutput; |         std::auto_ptr<Sound_Output> mOutput; | ||||||
| 
 | 
 | ||||||
|         // Caches available music tracks by <playlist name, (sound files) >
 |         // Caches available music tracks by <playlist name, (sound files) >
 | ||||||
|  | @ -56,6 +60,13 @@ namespace MWSound | ||||||
|         float mVoiceVolume; |         float mVoiceVolume; | ||||||
|         float mFootstepsVolume; |         float mFootstepsVolume; | ||||||
| 
 | 
 | ||||||
|  |         int mNearWaterRadius; | ||||||
|  |         int mNearWaterPoints; | ||||||
|  |         float mNearWaterIndoorTolerance; | ||||||
|  |         float mNearWaterOutdoorTolerance; | ||||||
|  |         std::string mNearWaterIndoorID; | ||||||
|  |         std::string mNearWaterOutdoorID; | ||||||
|  | 
 | ||||||
|         typedef std::auto_ptr<std::deque<Sound_Buffer> > SoundBufferList; |         typedef std::auto_ptr<std::deque<Sound_Buffer> > SoundBufferList; | ||||||
|         // List of sound buffers, grown as needed. New enties are added to the
 |         // List of sound buffers, grown as needed. New enties are added to the
 | ||||||
|         // back, allowing existing Sound_Buffer references/pointers to remain
 |         // back, allowing existing Sound_Buffer references/pointers to remain
 | ||||||
|  | @ -94,6 +105,7 @@ namespace MWSound | ||||||
|         int mPausedSoundTypes; |         int mPausedSoundTypes; | ||||||
| 
 | 
 | ||||||
|         MWBase::SoundPtr mUnderwaterSound; |         MWBase::SoundPtr mUnderwaterSound; | ||||||
|  |         MWBase::SoundPtr mNearWaterSound; | ||||||
| 
 | 
 | ||||||
|         Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); |         Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); | ||||||
| 
 | 
 | ||||||
|  | @ -108,6 +120,7 @@ namespace MWSound | ||||||
|         void streamMusicFull(const std::string& filename); |         void streamMusicFull(const std::string& filename); | ||||||
|         void updateSounds(float duration); |         void updateSounds(float duration); | ||||||
|         void updateRegionSound(float duration); |         void updateRegionSound(float duration); | ||||||
|  |         void updateWaterSound(float duration); | ||||||
| 
 | 
 | ||||||
|         float volumeFromType(PlayType type) const; |         float volumeFromType(PlayType type) const; | ||||||
| 
 | 
 | ||||||
|  | @ -119,7 +132,7 @@ namespace MWSound | ||||||
|         friend class OpenAL_Output; |         friend class OpenAL_Output; | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|         SoundManager(const VFS::Manager* vfs, bool useSound); |         SoundManager(const VFS::Manager* vfs, const std::map<std::string, std::string>& fallbackMap, bool useSound); | ||||||
|         virtual ~SoundManager(); |         virtual ~SoundManager(); | ||||||
| 
 | 
 | ||||||
|         virtual void processChangedSettings(const Settings::CategorySettingVector& settings); |         virtual void processChangedSettings(const Settings::CategorySettingVector& settings); | ||||||
|  |  | ||||||
|  | @ -249,7 +249,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot | ||||||
|                 +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() |                 +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() | ||||||
|                 +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() |                 +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() | ||||||
|                 +MWBase::Environment::get().getWindowManager()->countSavedGameRecords() |                 +MWBase::Environment::get().getWindowManager()->countSavedGameRecords() | ||||||
|                 +MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords(); |                 +MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords() | ||||||
|  |                 +MWBase::Environment::get().getInputManager()->countSavedGameRecords(); | ||||||
|         writer.setRecordCount (recordCount); |         writer.setRecordCount (recordCount); | ||||||
| 
 | 
 | ||||||
|         writer.save (stream); |         writer.save (stream); | ||||||
|  | @ -271,6 +272,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot | ||||||
|         MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener); |         MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener); | ||||||
|         MWBase::Environment::get().getWindowManager()->write(writer, listener); |         MWBase::Environment::get().getWindowManager()->write(writer, listener); | ||||||
|         MWBase::Environment::get().getMechanicsManager()->write(writer, listener); |         MWBase::Environment::get().getMechanicsManager()->write(writer, listener); | ||||||
|  |         MWBase::Environment::get().getInputManager()->write(writer, listener); | ||||||
| 
 | 
 | ||||||
|         // Ensure we have written the number of records that was estimated
 |         // Ensure we have written the number of records that was estimated
 | ||||||
|         if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record
 |         if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record
 | ||||||
|  | @ -462,6 +464,10 @@ void MWState::StateManager::loadGame (const Character *character, const std::str | ||||||
|                     MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.intval); |                     MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.intval); | ||||||
|                     break; |                     break; | ||||||
| 
 | 
 | ||||||
|  |                 case ESM::REC_INPU: | ||||||
|  |                     MWBase::Environment::get().getInputManager()->readRecord(reader, n.intval); | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|                 default: |                 default: | ||||||
| 
 | 
 | ||||||
|                     // ignore invalid records
 |                     // ignore invalid records
 | ||||||
|  |  | ||||||
|  | @ -93,6 +93,24 @@ namespace MWWorld | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void CellRef::applyChargeRemainderToBeSubtracted(float chargeRemainder) | ||||||
|  |     { | ||||||
|  |         mCellRef.mChargeIntRemainder += std::abs(chargeRemainder); | ||||||
|  |         if (mCellRef.mChargeIntRemainder > 1.0f) | ||||||
|  |         { | ||||||
|  |             float newChargeRemainder = (mCellRef.mChargeIntRemainder - std::floor(mCellRef.mChargeIntRemainder)); | ||||||
|  |             if (mCellRef.mChargeInt <= static_cast<int>(mCellRef.mChargeIntRemainder)) | ||||||
|  |             { | ||||||
|  |                 mCellRef.mChargeInt = 0; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 mCellRef.mChargeInt -= static_cast<int>(mCellRef.mChargeIntRemainder); | ||||||
|  |             } | ||||||
|  |             mCellRef.mChargeIntRemainder = newChargeRemainder; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     float CellRef::getChargeFloat() const |     float CellRef::getChargeFloat() const | ||||||
|     { |     { | ||||||
|         return mCellRef.mChargeFloat; |         return mCellRef.mChargeFloat; | ||||||
|  |  | ||||||
|  | @ -65,6 +65,7 @@ namespace MWWorld | ||||||
|         float getChargeFloat() const; // Implemented as union with int charge
 |         float getChargeFloat() const; // Implemented as union with int charge
 | ||||||
|         void setCharge(int charge); |         void setCharge(int charge); | ||||||
|         void setChargeFloat(float charge); |         void setChargeFloat(float charge); | ||||||
|  |         void applyChargeRemainderToBeSubtracted(float chargeRemainder); // Stores remainders and applies if > 1
 | ||||||
| 
 | 
 | ||||||
|         // The NPC that owns this object (and will get angry if you steal it)
 |         // The NPC that owns this object (and will get angry if you steal it)
 | ||||||
|         std::string getOwner() const; |         std::string getOwner() const; | ||||||
|  |  | ||||||
|  | @ -204,6 +204,14 @@ namespace MWWorld | ||||||
|         return (ref.mRef.mRefnum == pRefnum); |         return (ref.mRef.mRefnum == pRefnum); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Ptr CellStore::getCurrentPtr(LiveCellRefBase *ref) | ||||||
|  |     { | ||||||
|  |         MovedRefTracker::iterator found = mMovedToAnotherCell.find(ref); | ||||||
|  |         if (found != mMovedToAnotherCell.end()) | ||||||
|  |             return Ptr(ref, found->second); | ||||||
|  |         return Ptr(ref, this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void CellStore::moveFrom(const Ptr &object, CellStore *from) |     void CellStore::moveFrom(const Ptr &object, CellStore *from) | ||||||
|     { |     { | ||||||
|         if (mState != State_Loaded) |         if (mState != State_Loaded) | ||||||
|  | @ -964,26 +972,26 @@ namespace MWWorld | ||||||
|                 mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); |                 mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); | ||||||
|                 for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) |                 for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) | ||||||
|                 { |                 { | ||||||
|                     Ptr ptr (&*it, this); |                     Ptr ptr = getCurrentPtr(&*it); | ||||||
|                     ptr.getClass().respawn(ptr); |                     ptr.getClass().respawn(ptr); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) |             for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) | ||||||
|             { |             { | ||||||
|                 Ptr ptr (&*it, this); |                 Ptr ptr = getCurrentPtr(&*it); | ||||||
|                 clearCorpse(ptr); |                 clearCorpse(ptr); | ||||||
|                 ptr.getClass().respawn(ptr); |                 ptr.getClass().respawn(ptr); | ||||||
|             } |             } | ||||||
|             for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) |             for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) | ||||||
|             { |             { | ||||||
|                 Ptr ptr (&*it, this); |                 Ptr ptr = getCurrentPtr(&*it); | ||||||
|                 clearCorpse(ptr); |                 clearCorpse(ptr); | ||||||
|                 ptr.getClass().respawn(ptr); |                 ptr.getClass().respawn(ptr); | ||||||
|             } |             } | ||||||
|             for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it) |             for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it) | ||||||
|             { |             { | ||||||
|                 Ptr ptr (&*it, this); |                 Ptr ptr = getCurrentPtr(&*it); | ||||||
|                 // no need to clearCorpse, handled as part of mCreatures
 |                 // no need to clearCorpse, handled as part of mCreatures
 | ||||||
|                 ptr.getClass().respawn(ptr); |                 ptr.getClass().respawn(ptr); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -111,6 +111,9 @@ namespace MWWorld | ||||||
|             // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell
 |             // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell
 | ||||||
|             std::vector<LiveCellRefBase*> mMergedRefs; |             std::vector<LiveCellRefBase*> mMergedRefs; | ||||||
| 
 | 
 | ||||||
|  |             // Get the Ptr for the given ref which originated from this cell (possibly moved to another cell at this point).
 | ||||||
|  |             Ptr getCurrentPtr(MWWorld::LiveCellRefBase* ref); | ||||||
|  | 
 | ||||||
|             /// Moves object from the given cell to this cell.
 |             /// Moves object from the given cell to this cell.
 | ||||||
|             void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from); |             void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -393,7 +393,26 @@ namespace MWWorld | ||||||
| 
 | 
 | ||||||
|     bool Class::isPureWaterCreature(const MWWorld::Ptr& ptr) const |     bool Class::isPureWaterCreature(const MWWorld::Ptr& ptr) const | ||||||
|     { |     { | ||||||
|         return canSwim(ptr) && !canWalk(ptr); |         return canSwim(ptr) | ||||||
|  |                 && !isBipedal(ptr) | ||||||
|  |                 && !canFly(ptr) | ||||||
|  |                 && !canWalk(ptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool Class::isPureFlyingCreature(const Ptr& ptr) const | ||||||
|  |     { | ||||||
|  |         return canFly(ptr) | ||||||
|  |                 && !isBipedal(ptr) | ||||||
|  |                 && !canSwim(ptr) | ||||||
|  |                 && !canWalk(ptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool Class::isPureLandCreature(const Ptr& ptr) const | ||||||
|  |     { | ||||||
|  |         return canWalk(ptr) | ||||||
|  |                 && !isBipedal(ptr) | ||||||
|  |                 && !canFly(ptr) | ||||||
|  |                 && !canSwim(ptr); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool Class::isMobile(const MWWorld::Ptr& ptr) const |     bool Class::isMobile(const MWWorld::Ptr& ptr) const | ||||||
|  |  | ||||||
|  | @ -312,6 +312,8 @@ namespace MWWorld | ||||||
|             virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; |             virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; | ||||||
|             virtual bool canWalk(const MWWorld::ConstPtr& ptr) const; |             virtual bool canWalk(const MWWorld::ConstPtr& ptr) const; | ||||||
|             bool isPureWaterCreature(const MWWorld::Ptr& ptr) const; |             bool isPureWaterCreature(const MWWorld::Ptr& ptr) const; | ||||||
|  |             bool isPureFlyingCreature(const MWWorld::Ptr& ptr) const; | ||||||
|  |             bool isPureLandCreature(const MWWorld::Ptr& ptr) const; | ||||||
|             bool isMobile(const MWWorld::Ptr& ptr) const; |             bool isMobile(const MWWorld::Ptr& ptr) const; | ||||||
| 
 | 
 | ||||||
|             virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; |             virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; | ||||||
|  |  | ||||||
|  | @ -553,14 +553,14 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW | ||||||
|             if(listInMap != allowedForReplace.end()) |             if(listInMap != allowedForReplace.end()) | ||||||
|                 restockNum -= std::min(restockNum, listInMap->second); |                 restockNum -= std::min(restockNum, listInMap->second); | ||||||
|             //restock
 |             //restock
 | ||||||
|             addInitialItem(itemOrList, owner, restockNum, true); |             addInitialItem(itemOrList, owner, -restockNum, true); | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             //Restocking static item - just restock to the max count
 |             //Restocking static item - just restock to the max count
 | ||||||
|             int currentCount = count(itemOrList); |             int currentCount = count(itemOrList); | ||||||
|             if (currentCount < std::abs(it->mCount)) |             if (currentCount < std::abs(it->mCount)) | ||||||
|                 addInitialItem(itemOrList, owner, std::abs(it->mCount) - currentCount, true); |                 addInitialItem(itemOrList, owner, -(std::abs(it->mCount) - currentCount), true); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     flagAsModified(); |     flagAsModified(); | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| #include <components/esm/loadench.hpp> | #include <components/esm/loadench.hpp> | ||||||
| #include <components/esm/inventorystate.hpp> | #include <components/esm/inventorystate.hpp> | ||||||
| #include <components/misc/rng.hpp> | #include <components/misc/rng.hpp> | ||||||
|  | #include <components/settings/settings.hpp> | ||||||
| 
 | 
 | ||||||
| #include "../mwbase/environment.hpp" | #include "../mwbase/environment.hpp" | ||||||
| #include "../mwbase/world.hpp" | #include "../mwbase/world.hpp" | ||||||
|  | @ -214,79 +215,57 @@ 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; | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // 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(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; |  | ||||||
|         } |  | ||||||
|         int testSkill = test.getClass().getEquipmentSkill (test); |  | ||||||
| 
 |  | ||||||
|         std::pair<std::vector<int>, bool> itemsSlots = |  | ||||||
|             iter->getClass().getEquipmentSlots (*iter); |  | ||||||
| 
 |  | ||||||
|         for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin()); |  | ||||||
|             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()) |  | ||||||
|             { |  | ||||||
|                 Ptr old = *slots_.at (*iter2); |  | ||||||
| 
 |  | ||||||
|                 // check skill
 |  | ||||||
|                 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)) |  | ||||||
|                         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; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|         switch(test.getClass().canBeEquipped (test, actor).first) |         switch(test.getClass().canBeEquipped (test, actor).first) | ||||||
|         { |         { | ||||||
|  | @ -296,6 +275,47 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (iter.getType() == ContainerStore::Type_Armor && | ||||||
|  |                 test.getClass().getEffectiveArmorRating(test, actor) <= std::max(unarmoredRating, 0.f)) | ||||||
|  |         { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         std::pair<std::vector<int>, bool> itemsSlots = | ||||||
|  |             iter->getClass().getEquipmentSlots (*iter); | ||||||
|  | 
 | ||||||
|  |         for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin()); | ||||||
|  |             iter2!=itemsSlots.first.end(); ++iter2) | ||||||
|  |         { | ||||||
|  |             if (slots_.at (*iter2)!=end()) | ||||||
|  |             { | ||||||
|  |                 Ptr old = *slots_.at (*iter2); | ||||||
|  | 
 | ||||||
|  |                 if (iter.getType() == ContainerStore::Type_Armor) | ||||||
|  |                 { | ||||||
|  |                     if (old.getTypeName() == typeid(ESM::Armor).name()) | ||||||
|  |                     { | ||||||
|  |                         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; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             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
 | ||||||
|             { |             { | ||||||
|                 // unstack item pointed to by iterator if required
 |                 // unstack item pointed to by iterator if required
 | ||||||
|  | @ -310,6 +330,99 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     static const ESM::Skill::SkillEnum weaponSkills[] = | ||||||
|  |     { | ||||||
|  |         ESM::Skill::LongBlade, | ||||||
|  |         ESM::Skill::Axe, | ||||||
|  |         ESM::Skill::Spear, | ||||||
|  |         ESM::Skill::ShortBlade, | ||||||
|  |         ESM::Skill::Marksman, | ||||||
|  |         ESM::Skill::BluntWeapon | ||||||
|  |     }; | ||||||
|  |     const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]); | ||||||
|  | 
 | ||||||
|  |     bool weaponSkillVisited[weaponSkillsLength] = { false }; | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i) | ||||||
|  |     { | ||||||
|  |         int max = 0; | ||||||
|  |         int maxWeaponSkill = -1; | ||||||
|  | 
 | ||||||
|  |         for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j) | ||||||
|  |         { | ||||||
|  |             int skillValue = stats.getSkill(static_cast<int>(weaponSkills[j])).getModified(); | ||||||
|  | 
 | ||||||
|  |             if (skillValue > max && !weaponSkillVisited[j]) | ||||||
|  |             { | ||||||
|  |                 max = skillValue; | ||||||
|  |                 maxWeaponSkill = j; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (maxWeaponSkill == -1) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         max = 0; | ||||||
|  |         ContainerStoreIterator weapon(end()); | ||||||
|  | 
 | ||||||
|  |         for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter) | ||||||
|  |         { | ||||||
|  |             if (!canActorAutoEquip(actor, *iter)) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase; | ||||||
|  | 
 | ||||||
|  |             if (esmWeapon->mData.mType == ESM::Weapon::Arrow || esmWeapon->mData.mType == ESM::Weapon::Bolt) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill]) | ||||||
|  |             { | ||||||
|  |                 if (esmWeapon->mData.mChop[1] >= max) | ||||||
|  |                 { | ||||||
|  |                     max = esmWeapon->mData.mChop[1]; | ||||||
|  |                     weapon = iter; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (esmWeapon->mData.mSlash[1] >= max) | ||||||
|  |                 { | ||||||
|  |                     max = esmWeapon->mData.mSlash[1]; | ||||||
|  |                     weapon = iter; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (esmWeapon->mData.mThrust[1] >= max) | ||||||
|  |                 { | ||||||
|  |                     max = esmWeapon->mData.mThrust[1]; | ||||||
|  |                     weapon = iter; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first) | ||||||
|  |         { | ||||||
|  |             std::pair<std::vector<int>, bool> itemsSlots = | ||||||
|  |                 weapon->getClass().getEquipmentSlots (*weapon); | ||||||
|  | 
 | ||||||
|  |             for (std::vector<int>::const_iterator slot (itemsSlots.first.begin()); | ||||||
|  |                 slot!=itemsSlots.first.end(); ++slot) | ||||||
|  |             { | ||||||
|  |                 if (!itemsSlots.second) | ||||||
|  |                 { | ||||||
|  |                     if (weapon->getRefData().getCount() > 1) | ||||||
|  |                     { | ||||||
|  |                         unstack(*weapon, actor); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 slots_[*slot] = weapon; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         weaponSkillVisited[maxWeaponSkill] = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     bool changed = false; |     bool changed = false; | ||||||
| 
 | 
 | ||||||
|     for (std::size_t i=0; i<slots_.size(); ++i) |     for (std::size_t i=0; i<slots_.size(); ++i) | ||||||
|  | @ -402,8 +515,8 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) | ||||||
|                     MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( |                     MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( | ||||||
|                     effectIt->mEffectID); |                     effectIt->mEffectID); | ||||||
| 
 | 
 | ||||||
|                 // Fully resisted?
 |                 // Fully resisted or can't be applied to target?
 | ||||||
|                 if (params[i].mMultiplier == 0) |                 if (params[i].mMultiplier == 0 || !MWMechanics::checkEffectTarget(effectIt->mEffectID, actor, actor, actor == MWMechanics::getPlayer())) | ||||||
|                     continue; |                     continue; | ||||||
| 
 | 
 | ||||||
|                 float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; |                 float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; | ||||||
|  | @ -657,6 +770,9 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito | ||||||
|         for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin()); |         for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin()); | ||||||
|             effectIt!=enchantment.mEffects.mList.end(); ++effectIt) |             effectIt!=enchantment.mEffects.mList.end(); ++effectIt) | ||||||
|         { |         { | ||||||
|  |             // Don't get spell icon display information for enchantments that weren't actually applied
 | ||||||
|  |             if (mMagicEffects.get(MWMechanics::EffectKey(*effectIt)).getMagnitude() == 0) | ||||||
|  |                 continue; | ||||||
|             const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i]; |             const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i]; | ||||||
|             float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; |             float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; | ||||||
|             magnitude *= params.mMultiplier; |             magnitude *= params.mMultiplier; | ||||||
|  |  | ||||||
|  | @ -115,6 +115,8 @@ namespace MWWorld | ||||||
|             virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; |             virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; | ||||||
|             virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory); |             virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory); | ||||||
| 
 | 
 | ||||||
|  |             bool canActorAutoEquip(const MWWorld::Ptr& actor, const MWWorld::Ptr& item); | ||||||
|  | 
 | ||||||
|         public: |         public: | ||||||
| 
 | 
 | ||||||
|             InventoryStore(); |             InventoryStore(); | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
		Reference in a new issue