From 0874d69546bf33c7527181f9788a469213cf7207 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 24 Nov 2016 17:07:52 +0100 Subject: [PATCH 01/46] increased version number --- CMakeLists.txt | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d74e8cbc..6a74dde26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ endif() message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 40) +set(OPENMW_VERSION_MINOR 41) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/README.md b/README.md index 3a6c4a75a..d38dfaeb2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ OpenMW is a recreation of the engine for the popular role-playing game Morrowind OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set. -* Version: 0.40.0 +* Version: 0.41.0 * License: GPLv3 (see [docs/license/GPL3.txt](https://github.com/OpenMW/openmw/blob/master/docs/license/GPL3.txt) for more information) * Website: http://www.openmw.org * IRC: #openmw on irc.freenode.net From f5943ccd5d78fa7e7575a9c0b49960446fe993db Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 24 Nov 2016 17:09:53 +0100 Subject: [PATCH 02/46] updated changelog --- CHANGELOG.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0add2e02e..54b4bd34e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,78 @@ +0.41.0 +------ + + Bug #1138: Casting water walking doesn't move the player out of the water + Bug #1931: Rocks from blocked passage in Bamz-Amschend, Radacs Forge can reset and cant be removed again. + Bug #2048: Almvisi and Divine Intervention display wrong spell effect + Bug #2054: Show effect-indicator for "instant effect" spells and potions + Bug #2150: Clockwork City door animation problem + Bug #2288: Playback of weapon idle animation not correct + Bug #2410: Stat-review window doesn't display starting spells, powers, or abilities + Bug #2493: Repairing occasionally very slow + Bug #2716: [OSG] Water surface is too transparent from some angles + Bug #2859: [MAC OS X] Cannot exit fullscreen once enabled + Bug #3091: Editor: will not save addon if global variable value type is null + Bug #3277: Editor: Disabled nested tables in subviews aren't scrollable + Bug #3348: Disabled map markers show on minimap + Bug #3350: Extending selection to instances with same object results in duplicates. + Bug #3353: [Mod] Romance version 3.7 script failed + Bug #3376: [Mod] Vampire Embrace script fails to execute + Bug #3385: Banners don't animate in stormy weather as they do in the original game + Bug #3393: Akulakhan re-enabled after main quest + Bug #3427: Editor: OpenMW-CS instances won´t get deleted + Bug #3451: Feril Salmyn corpse isn't where it is supposed to be + Bug #3497: Zero-weight armor is displayed as "heavy" in inventory tooltip + Bug #3499: Idle animations don't always loop + Bug #3500: Spark showers at Sotha Sil do not appear until you look at the ceiling + Bug #3515: Editor: Moved objects in interior cells are teleported to exterior cells. + Bug #3520: Editor: OpenMW-CS cannot find project file when launching the game + Bug #3521: Armed NPCs don't use correct melee attacks + Bug #3535: Changing cell immediately after dying causes character to freeze. + Bug #3542: Unable to rest if unalerted slaughterfish are in the cell with you + Bug #3549: Blood effects occur even when a hit is resisted + Bug #3551: NPC Todwendy in german version can't interact + Bug #3552: Opening the journal when fonts are missing results in a crash + Bug #3555: SetInvisible command should not apply graphic effect + Bug #3561: Editor: changes from omwaddon are not loaded in [New Addon] mode + Bug #3562: Non-hostile NPCs can be disarmed by stealing their weapons via sneaking + Bug #3564: Editor: openmw-cs verification results + Bug #3568: Items that should be invisible are shown in the inventory + Bug #3574: Alchemy: Alembics and retorts are used in reverse + Bug #3575: Diaglog choices don't work in mw 0.40 + Bug #3576: Minor differences in AI reaction to hostile spell effects + Bug #3577: not local nolore dialog test + Bug #3578: Animation Replacer hangs after one cicle/step + Bug #3579: Bound Armor skillups and sounds + Bug #3583: Targetted GetCurrentAiPackage returns 0 + Bug #3584: Persuasion bug + Bug #3590: Vendor, Ilen Faveran, auto equips items from stock + Bug #3594: Weather doesn't seem to update correctly in Mournhold + Bug #3598: Saving doesn't save status of objects + Bug #3600: Screen goes black when trying to travel to Sadrith Mora + Bug #3608: Water ripples aren't created when walking on water + Bug #3626: Argonian NPCs swim like khajiits + Bug #3627: Cannot delete "Blessed touch" spell from spellbook + Bug #3634: An enchanted throwing weapon consumes charges from the stack in your inventory. (0.40.0) + Bug #3635: Levelled items in merchants are "re-rolled" (not bug 2952, see inside) + Feature #1118: AI combat: flee + Feature #1596: Editor: Render water + Feature #2042: Adding a non-portable Light to the inventory should cause the player to glow + Feature #3166: Editor: Instance editing mode - rotate sub mode + Feature #3167: Editor: Instance editing mode - scale sub mode + Feature #3420: ess-Importer: player control flags + Feature #3489: You shouldn't be be able to re-cast a bound equipment spell + Feature #3496: Zero-weight boots should play light boot footsteps + Feature #3516: Water Walking should give a "can't cast" message and fail when you are too deep + Feature #3519: Play audio and visual effects for all effects in a spell + Feature #3527: Double spell explosion scaling + Feature #3534: Play particle textures for spell effects + Feature #3539: Make NPCs use opponent's weapon range to decide whether to dodge + Feature #3540: Allow dodging for creatures with "biped" flag + Feature #3545: Drop shadow for items in menu + Feature #3558: Implement same spell range for "on touch" spells as original engine + Feature #3560: Allow using telekinesis with touch spells on objects + Task #3585: Some objects added by Morrowind Rebirth do not display properly their texture + 0.40.0 ------ From ee7f5d7d851c24d9bf795c4ff332097309404c74 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Fri, 28 Oct 2016 15:39:27 +0200 Subject: [PATCH 03/46] [macOS] Move all configs & resources into app bundles Fixes #3566 (https://bugs.openmw.org/issues/3566). --- CMakeLists.txt | 38 ++++++++++++++++++---------------- apps/launcher/main.cpp | 8 ------- apps/opencs/CMakeLists.txt | 33 ++++++++++++++++++++++++++--- apps/opencs/main.cpp | 5 ----- apps/openmw/CMakeLists.txt | 15 ++++++++++++++ apps/wizard/main.cpp | 6 ------ components/files/macospath.cpp | 2 +- files/mygui/CMakeLists.txt | 6 +++++- files/shaders/CMakeLists.txt | 6 +++++- 9 files changed, 76 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a74dde26..d19745ff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,7 @@ endif() # Set up common paths if (APPLE) 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) # Paths SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") @@ -286,6 +286,11 @@ endif (APPLE) # Set up DEBUG define 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/) # Specify build paths @@ -307,11 +312,15 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") -configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local - "${OpenMW_BINARY_DIR}/openmw.cfg") - -configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg - "${OpenMW_BINARY_DIR}/openmw.cfg.install") +if (NOT APPLE) + configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local + "${OpenMW_BINARY_DIR}/openmw.cfg") + configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg + "${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 "${OpenMW_BINARY_DIR}/openmw-cs.cfg") @@ -714,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) endif () - set(INSTALL_SUBDIR OpenMW) - - 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) + install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) @@ -729,8 +731,8 @@ if (APPLE) set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) - set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") - set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") + set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_NAME}") + set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${OPENCS_BUNDLE_NAME}") install(CODE " set(BU_CHMOD_BUNDLE_ITEMS ON) @@ -774,8 +776,8 @@ if (APPLE) set(${plugins_var} ${PLUGINS} PARENT_SCOPE) endfunction (install_plugins_for_bundle) - install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS) - install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) + install_plugins_for_bundle("${APP_BUNDLE_NAME}" 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(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index eadec64d0..96cadc8a7 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -35,14 +35,6 @@ int main(int argc, char *argv[]) // Now we make sure the current dir is set to application path QDir dir(QCoreApplication::applicationDirPath()); - #ifdef Q_OS_MAC - if (dir.dirName() == "MacOS") { - dir.cdUp(); - dir.cdUp(); - dir.cdUp(); - } - #endif - QDir::setCurrent(dir.absolutePath()); Launcher::MainDialog mainWin; diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index acf3cf021..e7e19be94 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -162,9 +162,15 @@ endif() include_directories(${CMAKE_CURRENT_BINARY_DIR}) 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() set (OPENCS_MAC_ICON "") + set (OPENCS_CFG "") + set (OPENCS_DEFAULT_FILTERS_FILE "") + set (OPENCS_OPENMW_CFG "") endif(APPLE) add_executable(openmw-cs @@ -174,12 +180,23 @@ add_executable(openmw-cs ${OPENCS_MOC_SRC} ${OPENCS_RES_SRC} ${OPENCS_MAC_ICON} + ${OPENCS_CFG} + ${OPENCS_DEFAULT_FILTERS_FILE} + ${OPENCS_OPENMW_CFG} ) 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 RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" - OUTPUT_NAME "OpenMW-CS" + OUTPUT_NAME ${OPENCS_BUNDLE_NAME} MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns" MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" @@ -190,6 +207,16 @@ if(APPLE) set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES 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) target_link_libraries(openmw-cs @@ -236,5 +263,5 @@ endif (MSVC) if(APPLE) - INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) + INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE) endif() diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index c6fe34835..fc5e8fc7a 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -62,11 +62,6 @@ int main(int argc, char *argv[]) #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); - if (dir.dirName() == "MacOS") { - dir.cdUp(); - dir.cdUp(); - dir.cdUp(); - } QDir::setCurrent(dir.absolutePath()); #endif diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 452629fcf..ce859bc3e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -179,6 +179,21 @@ target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) endif() 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(IOKIT_FRAMEWORK IOKit) target_link_libraries(openmw ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) diff --git a/apps/wizard/main.cpp b/apps/wizard/main.cpp index c861a4ac8..e3624742a 100644 --- a/apps/wizard/main.cpp +++ b/apps/wizard/main.cpp @@ -20,12 +20,6 @@ int main(int argc, char *argv[]) QDir dir(QCoreApplication::applicationDirPath()); #ifdef Q_OS_MAC - if (dir.dirName() == "MacOS") { - dir.cdUp(); - dir.cdUp(); - dir.cdUp(); - } - // force Qt to load only LOCAL plugins, don't touch system Qt installation QDir pluginsPath(QCoreApplication::applicationDirPath()); pluginsPath.cdUp(); diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 237141960..b49b72e46 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -68,7 +68,7 @@ boost::filesystem::path MacOsPath::getCachePath() const boost::filesystem::path MacOsPath::getLocalPath() const { - return boost::filesystem::path("./"); + return boost::filesystem::path("../Resources/"); } boost::filesystem::path MacOsPath::getGlobalDataPath() const diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 7494d3ba2..6592de56e 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -1,6 +1,10 @@ +if (NOT DEFINED OPENMW_MYGUI_FILES_ROOT) + return() +endif() + # Copy resource files into the build directory set(SDIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) +set(DDIR ${OPENMW_MYGUI_FILES_ROOT}/resources/mygui) set(MYGUI_FILES core.skin diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index 0738b5783..5ca0d1e83 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -1,6 +1,10 @@ +if (NOT DEFINED OPENMW_SHADERS_ROOT) + return() +endif() + # Copy resource files into the build directory set(SDIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(DDIR ${OpenMW_BINARY_DIR}/resources/shaders) +set(DDIR ${OPENMW_SHADERS_ROOT}/resources/shaders) set(SHADER_FILES water_vertex.glsl From 4f2a64ae17b7ffba56b2088a2e4c6902bfc6c911 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 13 Nov 2016 15:40:54 +0100 Subject: [PATCH 04/46] [macOS] Set OpenMW working dir to /Contents/MacOS --- apps/openmw/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index cfe7fe305..43901d579 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -352,9 +352,8 @@ int main(int argc, char**argv) #endif #ifdef __APPLE__ - // FIXME: set current dir to bundle path - //boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path(); - //boost::filesystem::current_path(bundlePath); + boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0])); + boost::filesystem::current_path(binary_path.parent_path()); #endif engine.reset(new OMW::Engine(cfgMgr)); From 9d8275580b390e59958fa79dac625c3e1eb966b4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Dec 2016 02:25:05 +0100 Subject: [PATCH 05/46] Movement solver: performance improvement for the minimum stepping distance check, no need to waste time doing a second stepMove if we did not hit a slope or the step was already large enough to begin with. --- apps/openmw/mwphysics/physicssystem.cpp | 34 +++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index cc3ba40e1..fece18d20 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -69,7 +69,14 @@ namespace MWPhysics 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) { /* @@ -120,7 +127,7 @@ namespace MWPhysics stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld); if(stepper.mFraction < std::numeric_limits::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?) /* @@ -138,7 +145,7 @@ namespace MWPhysics */ tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld); if(tracer.mFraction < std::numeric_limits::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. @@ -156,22 +163,22 @@ namespace MWPhysics * ============================================== */ 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 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. // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing // NOTE: caller's variables 'position' & 'remainingTime' are modified here position = stepper.mEndPos; 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, - // or moved full sStepSize distance (FIXME: is this a bug?) - return false; + return Result_Blocked; } @@ -361,14 +368,15 @@ namespace MWPhysics osg::Vec3f oldPosition = newPosition; // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: stepMove modifies newPosition if successful - bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); - if (!result) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent + const float minStep = 10.f; + 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; 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 if (ptr.getClass().isPureWaterCreature(ptr) From e5c9a82a296a9e02b57b5277bfae0bc60d424ecb Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 5 Dec 2016 20:12:13 +0900 Subject: [PATCH 06/46] Fix fortify maximum magicka expiration (Fixes #3648) --- apps/openmw/mwmechanics/actors.cpp | 10 ++++++---- apps/openmw/mwmechanics/spellcasting.cpp | 7 +++++-- apps/openmw/mwmechanics/spellcasting.hpp | 4 +++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1a6a62fc3..2a5995e0c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -512,8 +512,8 @@ namespace MWMechanics if (magnitude > 0 && remainingTime > 0 && remainingTime < mDuration) { CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); - effectTick(creatureStats, mActor, key, magnitude * remainingTime); - creatureStats.getMagicEffects().add(key, -magnitude); + if (effectTick(creatureStats, mActor, key, magnitude * remainingTime)) + creatureStats.getMagicEffects().add(key, -magnitude); } } }; @@ -527,8 +527,10 @@ namespace MWMechanics if (duration > 0) { - // apply correct magnitude for tickable effects that have just expired, - // in case duration > remaining time of effect + // Apply correct magnitude for tickable effects that have just expired, + // 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); creatureStats.getActiveSpells().visitEffectSources(visitor); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 5c5fc3899..f06472103 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -1009,10 +1009,10 @@ namespace MWMechanics 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) - return; + return false; bool receivedMagicDamage = false; @@ -1144,10 +1144,13 @@ namespace MWMechanics case ESM::MagicEffect::RemoveCurse: actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); break; + default: + return false; } if (receivedMagicDamage && actor == getPlayer()) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); + return true; } } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 852ae79dc..6e25acf50 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -63,7 +63,9 @@ namespace MWMechanics 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 { From 19143f9bc7c48f8154ee1fcb86d879d11ef7334c Mon Sep 17 00:00:00 2001 From: Allofich Date: Tue, 6 Dec 2016 22:23:06 +0900 Subject: [PATCH 07/46] Require line of sight for AI attacks (Fixes #3646) --- apps/openmw/mwmechanics/aicombat.cpp | 54 +++++++++++++++------------- apps/openmw/mwmechanics/aicombat.hpp | 2 ++ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 80b343d4f..21eaa0de5 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -60,30 +60,30 @@ namespace MWMechanics FleeState_RunToDestination }; FleeState mFleeState; - bool mFleeLOS; - float mFleeUpdateLOSTimer; + bool mLOS; + float mUpdateLOSTimer; float mFleeBlindRunTimer; ESM::Pathgrid::Point mFleeDest; AiCombatStorage(): - mAttackCooldown(0), + mAttackCooldown(0.0f), mTimerReact(AI_REACTION_TIME), - mTimerCombatMove(0), + mTimerCombatMove(0.0f), mReadyToAttack(false), mAttack(false), - mAttackRange(0), + mAttackRange(0.0f), mCombatMove(false), mLastTargetPos(0,0,0), mCell(NULL), mCurrentAction(), - mActionCooldown(0), + mActionCooldown(0.0f), mStrength(), mForceNoShortcut(false), mShortcutFailPos(), mMovement(), mFleeState(FleeState_None), - mFleeLOS(false), - mFleeUpdateLOSTimer(0.0f), + mLOS(false), + mUpdateLOSTimer(0.0f), mFleeBlindRunTimer(0.0f) {} @@ -181,10 +181,14 @@ namespace MWMechanics if (!storage.isFleeing()) { - if (storage.mCurrentAction.get()) // need to wait to init action with it's attack range + if (storage.mCurrentAction.get()) // need to wait to init action with its attack range { - //Update every frame - 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; } @@ -283,7 +287,7 @@ namespace MWMechanics osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); - storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack); + storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS); if (storage.mReadyToAttack) { @@ -309,18 +313,23 @@ namespace MWMechanics } } - void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) + 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; - static const float BLIND_RUN_DURATION = 1.0f; - - if (storage.mFleeUpdateLOSTimer <= 0.f) + if (storage.mUpdateLOSTimer <= 0.f) { - storage.mFleeLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); - storage.mFleeUpdateLOSTimer = LOS_UPDATE_DURATION; + storage.mLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); + storage.mUpdateLOSTimer = LOS_UPDATE_DURATION; } else - storage.mFleeUpdateLOSTimer -= duration; + 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) @@ -332,7 +341,7 @@ namespace MWMechanics { float triggerDist = getMaxAttackDistance(target); - if (storage.mFleeLOS && + if (storage.mLOS && (triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist)) { const ESM::Pathgrid* pathgrid = @@ -399,7 +408,7 @@ namespace MWMechanics static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fFleeDistance")->getFloat(); float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length(); - if ((dist > fFleeDistance && !storage.mFleeLOS) + if ((dist > fFleeDistance && !storage.mLOS) || pathTo(actor, storage.mFleeDest, duration)) { state = AiCombatStorage::FleeState_Idle; @@ -602,9 +611,6 @@ namespace MWMechanics mMovement.mPosition[2] = 0; mFleeState = FleeState_None; mFleeDest = ESM::Pathgrid::Point(0, 0, 0); - mFleeLOS = false; - mFleeUpdateLOSTimer = 0.0f; - mFleeUpdateLOSTimer = 0.0f; } bool AiCombatStorage::isFleeing() diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 3f2bde776..a2e995cb3 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -61,6 +61,8 @@ namespace MWMechanics 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 From b5e8c98b40c95cda7e2489e5cde2bf143b765544 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Dec 2016 23:21:33 +0100 Subject: [PATCH 08/46] Enable waterCollision after moving the player above water (Fixes #3672) --- apps/openmw/mwphysics/physicssystem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index fece18d20..14bce06e3 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1349,7 +1349,8 @@ namespace MWPhysics else if (physicActor->getCollisionMode() && canMoveToWaterSurface(iter->first, waterlevel)) { const osg::Vec3f actorPosition = physicActor->getPosition(); - physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel)); + physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel)); + waterCollision = true; } } physicActor->setCanWaterWalk(waterCollision); From f2f0e9f1e71660d08bfc7df7509f7e3dc4faf246 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Dec 2016 20:09:20 +0100 Subject: [PATCH 09/46] Clear the Skeleton's bone cache when a node is added/removed (Fixes #3663) --- apps/openmw/mwrender/creatureanimation.cpp | 2 -- apps/openmw/mwrender/npcanimation.cpp | 2 -- components/sceneutil/skeleton.cpp | 12 ++++++++++++ components/sceneutil/skeleton.hpp | 6 ++++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index a095d5dd4..36a0f4085 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -110,8 +110,6 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(item.getClass().getModel(item)); osg::ref_ptr attached = SceneUtil::attach(node, mObjectRoot, bonename, bonename); mResourceSystem->getSceneManager()->notifyAttached(attached); - if (mSkeleton) - mSkeleton->markDirty(); scene.reset(new PartHolder(attached)); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d9dd1a89e..53eaf0996 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -671,8 +671,6 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st { osg::ref_ptr instance = mResourceSystem->getSceneManager()->getInstance(model); osg::ref_ptr attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, bonename); - if (mSkeleton) - mSkeleton->markDirty(); mResourceSystem->getSceneManager()->notifyAttached(attached); if (enchantedGlow) addGlow(attached, *glowColor); diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index 390a8e823..f3c2aef77 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -148,6 +148,8 @@ void Skeleton::markDirty() { mTraversedEvenFrame = false; mTraversedOddFrame = false; + mBoneCache.clear(); + mBoneCacheInit = false; } void Skeleton::traverse(osg::NodeVisitor& nv) @@ -160,6 +162,16 @@ void Skeleton::traverse(osg::NodeVisitor& nv) osg::Group::traverse(nv); } +void Skeleton::childInserted(unsigned int) +{ + markDirty(); +} + +void Skeleton::childRemoved(unsigned int, unsigned int) +{ + markDirty(); +} + Bone::Bone() : mNode(NULL) { diff --git a/components/sceneutil/skeleton.hpp b/components/sceneutil/skeleton.hpp index 764b7f645..24dcc6b3f 100644 --- a/components/sceneutil/skeleton.hpp +++ b/components/sceneutil/skeleton.hpp @@ -53,10 +53,12 @@ namespace SceneUtil bool getActive() const; - /// If a new RigGeometry is added after the Skeleton has already been rendered, you must call markDirty(). + void traverse(osg::NodeVisitor& nv); + void markDirty(); - void traverse(osg::NodeVisitor& nv); + virtual void childInserted(unsigned int); + virtual void childRemoved(unsigned int, unsigned int); private: // The root bone is not a "real" bone, it has no corresponding node in the scene graph. From 9c56ecac770dd22e107917840f59777b437d7b48 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 19 Dec 2016 15:28:27 +0100 Subject: [PATCH 10/46] fixed changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b4bd34e..87d826753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Bug #2716: [OSG] Water surface is too transparent from some angles Bug #2859: [MAC OS X] Cannot exit fullscreen once enabled Bug #3091: Editor: will not save addon if global variable value type is null - Bug #3277: Editor: Disabled nested tables in subviews aren't scrollable + Bug #3277: Editor: Non-functional nested tables in subviews need to be hidden instead of being disabled Bug #3348: Disabled map markers show on minimap Bug #3350: Extending selection to instances with same object results in duplicates. Bug #3353: [Mod] Romance version 3.7 script failed From 8902bb5b13bde2a465eca446e768f136ba6b593d Mon Sep 17 00:00:00 2001 From: NeveHanter Date: Tue, 20 Dec 2016 12:38:51 +0100 Subject: [PATCH 11/46] Player now pays for the following actors when travelling, with the exception of the first follower who travels for free, refactored getFollowers to getActorsFollowing/getActorsSidingWith --- apps/openmw/mwbase/mechanicsmanager.hpp | 5 ++++ apps/openmw/mwgui/travelwindow.cpp | 28 ++++++++++++++++--- apps/openmw/mwmechanics/actors.cpp | 14 ++++++++++ apps/openmw/mwmechanics/actors.hpp | 5 ++++ .../mwmechanics/mechanicsmanagerimp.cpp | 22 ++++++--------- .../mwmechanics/mechanicsmanagerimp.hpp | 5 ++++ apps/openmw/mwworld/actionteleport.cpp | 20 ++----------- 7 files changed, 64 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 0f414972f..bd0d2ea4a 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace osg @@ -200,6 +201,10 @@ namespace MWBase virtual std::list getEnemiesNearby(const MWWorld::Ptr& actor) = 0; + /// Recursive versions of above methods + virtual void getActorsFollowing(const MWWorld::Ptr& actor, std::set& out) = 0; + virtual void getActorsSidingWith(const MWWorld::Ptr& actor, std::set& out) = 0; + virtual void playerLoaded() = 0; virtual int countSavedGameRecords() const = 0; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 8c55c3732..80a622803 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -55,7 +55,7 @@ namespace MWGui void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior) { - int price = 0; + int price; const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); @@ -70,14 +70,34 @@ namespace MWGui else { ESM::Position PlayerPos = player.getRefData().getPosition(); - float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); + float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2)); price = static_cast(d / gmst.find("fTravelMult")->getFloat()); } - price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); + // Add price for the followers in range + std::set followers; + MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(player, followers); + + unsigned int travellingFollowers = 0; + for(std::set::iterator it = followers.begin();it != followers.end();++it) + { + MWWorld::Ptr follower = *it; + + std::string script = follower.getClass().getScript(follower); + if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1) + continue; + + if ((follower.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= 800*800) + ++travellingFollowers; + } + + // Apply followers cost, in vanilla one follower travels for free + price *= travellingFollowers; + + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); - toAdd->setEnabled(price<=playerGold); + toAdd->setEnabled(price <= playerGold); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 23a6f497f..af8a47ccf 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1482,6 +1482,20 @@ namespace MWMechanics return list; } + void Actors::getActorsFollowing(const MWWorld::Ptr &actor, std::set& out) { + std::list followers = getActorsFollowing(actor); + for(std::list::iterator it = followers.begin();it != followers.end();++it) + if (out.insert(*it).second) + getActorsFollowing(*it, out); + } + + void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out) { + std::list followers = getActorsSidingWith(actor); + for(std::list::iterator it = followers.begin();it != followers.end();++it) + if (out.insert(*it).second) + getActorsSidingWith(*it, out); + } + std::list Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor) { std::list list; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 163995f6f..20aef4c17 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -123,6 +123,11 @@ namespace MWMechanics std::list getActorsSidingWith(const MWWorld::Ptr& actor); std::list getActorsFollowing(const MWWorld::Ptr& actor); + /// Recursive version of getActorsFollowing + void getActorsFollowing(const MWWorld::Ptr &actor, std::set& out); + /// Recursive version of getActorsSidingWith + void getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out); + /// Get the list of AiFollow::mFollowIndex for all actors following this target std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 97fa98b3b..e552f4683 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -978,18 +978,6 @@ namespace MWMechanics } - void getFollowers (const MWWorld::Ptr& actor, std::set& out) - { - std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(actor); - for(std::list::iterator it = followers.begin();it != followers.end();++it) - { - if (out.insert(*it).second) - { - getFollowers(*it, out); - } - } - } - bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware) { // NOTE: victim may be empty @@ -1013,7 +1001,7 @@ namespace MWMechanics // get the player's followers / allies (works recursively) that will not report crimes std::set playerFollowers; - getFollowers(player, playerFollowers); + getActorsSidingWith(player, playerFollowers); // Did anyone see it? bool crimeSeen = false; @@ -1437,6 +1425,14 @@ namespace MWMechanics return mActors.getEnemiesNearby(actor); } + void MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor, std::set& out) { + mActors.getActorsFollowing(actor, out); + } + + void MechanicsManager::getActorsSidingWith(const MWWorld::Ptr& actor, std::set& out) { + mActors.getActorsSidingWith(actor, out); + } + int MechanicsManager::countSavedGameRecords() const { return 1 // Death counter diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 04c67fcb6..ed06f58c5 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -165,6 +165,11 @@ namespace MWMechanics virtual std::list getActorsFighting(const MWWorld::Ptr& actor); virtual std::list getEnemiesNearby(const MWWorld::Ptr& actor); + /// Recursive version of getActorsFollowing + virtual void getActorsFollowing(const MWWorld::Ptr& actor, std::set& out); + /// Recursive version of getActorsSidingWith + virtual void getActorsSidingWith(const MWWorld::Ptr& actor, std::set& out); + virtual bool toggleAI(); virtual bool isAIActive(); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index c9315283d..ab1c0afc6 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -8,23 +8,6 @@ #include "player.hpp" -namespace -{ - - void getFollowers (const MWWorld::Ptr& actor, std::set& out) - { - std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); - for(std::list::iterator it = followers.begin();it != followers.end();++it) - { - if (out.insert(*it).second) - { - getFollowers(*it, out); - } - } - } - -} - namespace MWWorld { ActionTeleport::ActionTeleport (const std::string& cellName, @@ -39,7 +22,8 @@ namespace MWWorld { //find any NPC that is following the actor and teleport him too std::set followers; - getFollowers(actor, followers); + MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers); + for(std::set::iterator it = followers.begin();it != followers.end();++it) { MWWorld::Ptr follower = *it; From 15cd3c178bff79a55b58e9489bfa846876e22723 Mon Sep 17 00:00:00 2001 From: NeveHanter Date: Tue, 20 Dec 2016 21:23:55 +0100 Subject: [PATCH 12/46] Clamp price multiplication to 1, as it resulted in player alone traveling at no fee. --- apps/openmw/mwgui/travelwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 80a622803..43a4efc7a 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -78,7 +78,7 @@ namespace MWGui std::set followers; MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(player, followers); - unsigned int travellingFollowers = 0; + int travellingFollowers = 0; for(std::set::iterator it = followers.begin();it != followers.end();++it) { MWWorld::Ptr follower = *it; @@ -92,7 +92,7 @@ namespace MWGui } // Apply followers cost, in vanilla one follower travels for free - price *= travellingFollowers; + price *= std::max(1, travellingFollowers); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); From 561e0cbbf986f889829d9c4d40b30ad6c2d8aca6 Mon Sep 17 00:00:00 2001 From: logzero Date: Tue, 6 Dec 2016 22:20:31 +0100 Subject: [PATCH 13/46] Use squared length for distance checks in movement solver. --- apps/openmw/mwphysics/physicssystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 544fb0199..e6ef5328c 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -229,7 +229,7 @@ namespace MWPhysics collisionWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit() && - ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length() > 35 + ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length2() > 35*35 || getSlope(tracer.mPlaneNormal) > sMaxSlope)) { actor->setOnGround(getSlope(toOsg(resultCallback1.m_hitNormalWorld)) <= sMaxSlope); @@ -370,7 +370,7 @@ namespace MWPhysics // NOTE: stepMove modifies newPosition if successful const float minStep = 10.f; 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 + if (result == Result_MaxSlope && (velocity*remainingTime).length2() < minStep*minStep) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent { osg::Vec3f normalizedVelocity = velocity; normalizedVelocity.normalize(); From 25a0219e4d937a36172aba24c7440d7be8b567b8 Mon Sep 17 00:00:00 2001 From: logzero Date: Tue, 6 Dec 2016 22:46:09 +0100 Subject: [PATCH 14/46] Use cosine of max slope angle for walkable slope checks in movement solver. --- apps/openmw/mwphysics/physicssystem.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index e6ef5328c..bf0589797 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -63,10 +63,11 @@ namespace MWPhysics class MovementSolver { private: - static float getSlope(osg::Vec3f normal) + template + static bool isWalkableSlope(const Vec3 &normal) { - normal.normalize(); - return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f))); + static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope)); + return (normal.z() > sMaxSlopeCos); } enum StepMoveResult @@ -163,7 +164,7 @@ namespace MWPhysics * ============================================== */ stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); - if (getSlope(stepper.mPlaneNormal) > sMaxSlope) + if (!isWalkableSlope(stepper.mPlaneNormal)) return Result_MaxSlope; if(stepper.mFraction < 1.0f) { @@ -230,13 +231,13 @@ namespace MWPhysics if (resultCallback1.hasHit() && ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length2() > 35*35 - || getSlope(tracer.mPlaneNormal) > sMaxSlope)) + || !isWalkableSlope(tracer.mPlaneNormal))) { - actor->setOnGround(getSlope(toOsg(resultCallback1.m_hitNormalWorld)) <= sMaxSlope); + actor->setOnGround(isWalkableSlope(resultCallback1.m_hitNormalWorld)); return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f); } - actor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope); + actor->setOnGround(isWalkableSlope(tracer.mPlaneNormal)); return tracer.mEndPos; } @@ -413,7 +414,7 @@ namespace MWPhysics osg::Vec3f to = newPosition - (physicActor->getOnGround() ? osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f)); tracer.doTrace(colobj, from, to, collisionWorld); - if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope + if(tracer.mFraction < 1.0f && isWalkableSlope(tracer.mPlaneNormal) && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor) { const btCollisionObject* standingOn = tracer.mHitObject; From 0b08802910fbcb2c54ee36dc53a77f4624fd51a7 Mon Sep 17 00:00:00 2001 From: logzero Date: Wed, 14 Dec 2016 16:30:31 +0100 Subject: [PATCH 15/46] Integrate MinStep move attempt into stepMove. This can save 1 to 3 convex casts per iteration. --- apps/openmw/mwphysics/physicssystem.cpp | 63 ++++++++++++++----------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index bf0589797..3699eeeba 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -55,6 +55,7 @@ namespace MWPhysics static const float sMaxSlope = 49.0f; static const float sStepSizeUp = 34.0f; static const float sStepSizeDown = 62.0f; + static const float sMinStep = 10.f; // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. static const int sMaxIterations = 8; @@ -63,6 +64,12 @@ namespace MWPhysics class MovementSolver { private: + static bool isActor(const btCollisionObject *obj) + { + assert(obj); + return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor; + } + template static bool isWalkableSlope(const Vec3 &normal) { @@ -70,14 +77,12 @@ namespace MWPhysics return (normal.z() > sMaxSlopeCos); } - enum StepMoveResult + static bool canStepDown(const ActorTracer &stepper) { - Result_Blocked, // unable to move over obstacle - Result_MaxSlope, // unable to end movement on this slope - Result_Success - }; + return stepper.mHitObject && isWalkableSlope(stepper.mPlaneNormal) && !isActor(stepper.mHitObject); + } - static StepMoveResult stepMove(const btCollisionObject *colobj, osg::Vec3f &position, + static bool stepMove(const btCollisionObject *colobj, osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld) { /* @@ -128,7 +133,7 @@ namespace MWPhysics stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld); if(stepper.mFraction < std::numeric_limits::epsilon()) - return Result_Blocked; // didn't even move the smallest representable amount + return false; // didn't even move the smallest representable amount // (TODO: shouldn't this be larger? Why bother with such a small amount?) /* @@ -144,9 +149,10 @@ namespace MWPhysics * +--+ * ============================================== */ - tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld); + osg::Vec3f tracerPos = stepper.mEndPos; + tracer.doTrace(colobj, tracerPos, tracerPos + toMove, collisionWorld); if(tracer.mFraction < std::numeric_limits::epsilon()) - return Result_Blocked; // didn't even move the smallest representable amount + return false; // didn't even move the smallest representable amount /* * Try moving back down sStepSizeDown using stepper. @@ -164,22 +170,32 @@ namespace MWPhysics * ============================================== */ stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); - if (!isWalkableSlope(stepper.mPlaneNormal)) - return Result_MaxSlope; - if(stepper.mFraction < 1.0f) + if (!canStepDown(stepper)) + { + // Try again with increased step length + if (tracer.mFraction < 1.0f || toMove.length2() > sMinStep*sMinStep) + return false; + + osg::Vec3f direction = toMove; + direction.normalize(); + tracer.doTrace(colobj, tracerPos, tracerPos + direction*sMinStep, collisionWorld); + if (tracer.mFraction < 0.001f) + return false; + + stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); + if (!canStepDown(stepper)) + return false; + } + if (stepper.mFraction < 1.0f) { - // don't allow stepping up other actors - if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor) - return Result_Blocked; // 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 // NOTE: caller's variables 'position' & 'remainingTime' are modified here position = stepper.mEndPos; remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance - return Result_Success; + return true; } - - return Result_Blocked; + return false; } @@ -369,15 +385,8 @@ namespace MWPhysics osg::Vec3f oldPosition = newPosition; // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: stepMove modifies newPosition if successful - const float minStep = 10.f; - StepMoveResult result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); - if (result == Result_MaxSlope && (velocity*remainingTime).length2() < minStep*minStep) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent - { - osg::Vec3f normalizedVelocity = velocity; - normalizedVelocity.normalize(); - result = stepMove(colobj, newPosition, normalizedVelocity*minStep, remainingTime, collisionWorld); - } - if(result == Result_Success) + bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); + if (result) { // don't let pure water creatures move out of water after stepMove if (ptr.getClass().isPureWaterCreature(ptr) From 4f6e65e48105d97ce6886bdecdc29cd253b3f061 Mon Sep 17 00:00:00 2001 From: logzero Date: Thu, 15 Dec 2016 13:56:08 +0100 Subject: [PATCH 16/46] Apply sliding upward check to new velocity. This helps to capture the case where new velocity only differs in the z component (normal pointing up). TODO: Find a better way to handle the normal pointing up case. --- apps/openmw/mwphysics/physicssystem.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 3699eeeba..0d0a6e9f9 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -402,17 +402,18 @@ namespace MWPhysics reflectdir.normalize(); osg::Vec3f newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + + // Do not allow sliding upward if there is gravity. + // Stepping will have taken care of that. + if(!(newPosition.z() < swimlevel || isFlying)) + newVelocity.z() = std::min(newVelocity.z(), 0.0f); + if ((newVelocity-velocity).length2() < 0.01) break; if ((velocity * origVelocity) <= 0.f) break; // ^ dot product velocity = newVelocity; - - // Do not allow sliding upward if there is gravity. Stepping will have taken - // care of that. - if(!(newPosition.z() < swimlevel || isFlying)) - velocity.z() = std::min(velocity.z(), 0.0f); } } From 50fd9130587a9c6864a34db25c1369657c61b380 Mon Sep 17 00:00:00 2001 From: logzero Date: Wed, 21 Dec 2016 10:41:43 +0100 Subject: [PATCH 17/46] Refactor stepMove function into a Stepper object to be able to reuse up stepper results for successive movement solver iterations. This can reduce the number of convex casts almost by half in some cases. --- apps/openmw/mwphysics/physicssystem.cpp | 99 +++++++++++++++---------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 0d0a6e9f9..494ff681c 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -60,30 +60,41 @@ namespace MWPhysics // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. static const int sMaxIterations = 8; - // FIXME: move to a separate file - class MovementSolver + static bool isActor(const btCollisionObject *obj) + { + assert(obj); + return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor; + } + + template + static bool isWalkableSlope(const Vec3 &normal) + { + static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope)); + return (normal.z() > sMaxSlopeCos); + } + + static bool canStepDown(const ActorTracer &stepper) + { + return stepper.mHitObject && isWalkableSlope(stepper.mPlaneNormal) && !isActor(stepper.mHitObject); + } + + class Stepper { private: - static bool isActor(const btCollisionObject *obj) - { - assert(obj); - return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor; - } + const btCollisionWorld *mColWorld; + const btCollisionObject *mColObj; - template - static bool isWalkableSlope(const Vec3 &normal) - { - static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope)); - return (normal.z() > sMaxSlopeCos); - } + ActorTracer mTracer, mUpStepper, mDownStepper; + bool mHaveMoved; - static bool canStepDown(const ActorTracer &stepper) - { - return stepper.mHitObject && isWalkableSlope(stepper.mPlaneNormal) && !isActor(stepper.mHitObject); - } + public: + Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj) + : mColWorld(colWorld) + , mColObj(colObj) + , mHaveMoved(true) + {} - static bool stepMove(const btCollisionObject *colobj, osg::Vec3f &position, - const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld) + bool step(osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime) { /* * Slide up an incline or set of stairs. Should be called only after a @@ -129,12 +140,14 @@ namespace MWPhysics * +--+ +-------- * ============================================== */ - ActorTracer tracer, stepper; - - stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld); - if(stepper.mFraction < std::numeric_limits::epsilon()) - return false; // didn't even move the smallest representable amount - // (TODO: shouldn't this be larger? Why bother with such a small amount?) + if (mHaveMoved) + { + mHaveMoved = false; + mUpStepper.doTrace(mColObj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), mColWorld); + if(mUpStepper.mFraction < std::numeric_limits::epsilon()) + return false; // didn't even move the smallest representable amount + // (TODO: shouldn't this be larger? Why bother with such a small amount?) + } /* * Try moving from the elevated position using tracer. @@ -149,9 +162,9 @@ namespace MWPhysics * +--+ * ============================================== */ - osg::Vec3f tracerPos = stepper.mEndPos; - tracer.doTrace(colobj, tracerPos, tracerPos + toMove, collisionWorld); - if(tracer.mFraction < std::numeric_limits::epsilon()) + osg::Vec3f tracerPos = mUpStepper.mEndPos; + mTracer.doTrace(mColObj, tracerPos, tracerPos + toMove, mColWorld); + if(mTracer.mFraction < std::numeric_limits::epsilon()) return false; // didn't even move the smallest representable amount /* @@ -169,36 +182,40 @@ namespace MWPhysics * +--+ +--+ * ============================================== */ - stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); - if (!canStepDown(stepper)) + mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld); + if (!canStepDown(mDownStepper)) { // Try again with increased step length - if (tracer.mFraction < 1.0f || toMove.length2() > sMinStep*sMinStep) + if (mTracer.mFraction < 1.0f || toMove.length2() > sMinStep*sMinStep) return false; osg::Vec3f direction = toMove; direction.normalize(); - tracer.doTrace(colobj, tracerPos, tracerPos + direction*sMinStep, collisionWorld); - if (tracer.mFraction < 0.001f) + mTracer.doTrace(mColObj, tracerPos, tracerPos + direction*sMinStep, mColWorld); + if (mTracer.mFraction < 0.001f) return false; - stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld); - if (!canStepDown(stepper)) + mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld); + if (!canStepDown(mDownStepper)) return false; } - if (stepper.mFraction < 1.0f) + if (mDownStepper.mFraction < 1.0f) { // 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 // NOTE: caller's variables 'position' & 'remainingTime' are modified here - position = stepper.mEndPos; - remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance + position = mDownStepper.mEndPos; + remainingTime *= (1.0f-mTracer.mFraction); // remaining time is proportional to remaining distance + mHaveMoved = true; return true; } return false; } + }; - + class MovementSolver + { + private: ///Project a vector u on another vector v static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v) { @@ -329,8 +346,8 @@ namespace MWPhysics velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); } + Stepper stepper(collisionWorld, colobj); osg::Vec3f origVelocity = velocity; - osg::Vec3f newPosition = position; /* * A loop to find newPosition using tracer, if successful different from the starting position. @@ -385,7 +402,7 @@ namespace MWPhysics osg::Vec3f oldPosition = newPosition; // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: stepMove modifies newPosition if successful - bool result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld); + bool result = stepper.step(newPosition, velocity*remainingTime, remainingTime); if (result) { // don't let pure water creatures move out of water after stepMove From cf496287f76026c97400c5f49584b6fccff37c87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Dec 2016 14:39:03 +0100 Subject: [PATCH 18/46] Silence static analysis warnings --- apps/openmw/mwgui/formatting.cpp | 4 +++- apps/openmw/mwphysics/physicssystem.cpp | 2 ++ apps/openmw/mwworld/inventorystore.cpp | 7 +++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index b7e24a7ee..72e1c09f3 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -428,7 +428,9 @@ namespace MWGui { // split lines const int lineHeight = currentFontHeight(); - unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()) / lineHeight; + unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()); + if (lineHeight > 0) + lastLine /= lineHeight; int ret = mPaginator.getCurrentTop() + lastLine * lineHeight; // first empty lines that would go to the next page should be ignored diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 544fb0199..7cfa38ff2 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -996,6 +996,8 @@ namespace MWPhysics bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel) { const Actor* physicActor = getActor(actor); + if (!physicActor) + return false; const float halfZ = physicActor->getHalfExtents().z(); const osg::Vec3f actorPosition = physicActor->getPosition(); const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 9f8bae280..d1b71bbd5 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -402,8 +402,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) std::pair, bool> itemsSlots = weapon->getClass().getEquipmentSlots (*weapon); - for (std::vector::const_iterator slot (itemsSlots.first.begin()); - slot!=itemsSlots.first.end(); ++slot) + if (!itemsSlots.first.empty()) { if (!itemsSlots.second) { @@ -413,8 +412,8 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) } } - slots_[*slot] = weapon; - break; + int slot = itemsSlots.first.front(); + slots_[slot] = weapon; } break; From 87fd011a283188b51e6865644cb6dfa2b4eb657d Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Thu, 22 Dec 2016 20:48:58 +0100 Subject: [PATCH 19/46] Clean-up saves by dropping references with invalid RefNums (Fixes #1956) --- apps/openmw/mwworld/cellstore.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 5e65bad7c..ff3427e12 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -137,6 +137,9 @@ namespace iter->load (state); return; } + + std::cerr << "Dropping reference to " << state.mRef.mRefID << " (invalid content file link)" << std::endl; + return; } // new reference From 5f234f89529f3b8dbe5d67ee22bb3099b7ab2a77 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Fri, 23 Dec 2016 19:03:59 +0100 Subject: [PATCH 20/46] Dialogue: "PC Rank" condition now uses speaker's faction if "PC Faction" is not given (Fixes #3689) --- apps/openmw/mwdialogue/filter.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 37a84b287..d51918152 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -107,11 +107,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const { const MWWorld::Ptr player = MWMechanics::getPlayer(); + MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player); - // check player faction + // check player faction and rank if (!info.mPcFaction.empty()) { - MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player); std::map::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction)); if(iter==stats.getFactionRanks().end()) @@ -121,6 +121,18 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const if (iter->second < info.mData.mPCrank) return false; } + else if (info.mData.mPCrank != -1) + { + // required PC faction is not specified but PC rank is; use speaker's faction + std::map::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (mActor.getClass().getPrimaryFaction(mActor))); + + if(iter==stats.getFactionRanks().end()) + return false; + + // check rank + if (iter->second < info.mData.mPCrank) + return false; + } // check cell if (!info.mCell.empty()) From ff4aba2a6e004914b329d063a2c3264e7ecdce63 Mon Sep 17 00:00:00 2001 From: NeveHanter Date: Fri, 23 Dec 2016 21:27:29 +0100 Subject: [PATCH 21/46] Moved duplicated code to common ActionTeleport static method and reordered travel price calculations --- apps/openmw/mwgui/travelwindow.cpp | 23 ++++------------- apps/openmw/mwworld/actionteleport.cpp | 35 +++++++++++++++----------- apps/openmw/mwworld/actionteleport.hpp | 6 ++++- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 43a4efc7a..3063b3268 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -74,27 +74,14 @@ namespace MWGui price = static_cast(d / gmst.find("fTravelMult")->getFloat()); } - // Add price for the followers in range - std::set followers; - MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(player, followers); - - int travellingFollowers = 0; - for(std::set::iterator it = followers.begin();it != followers.end();++it) - { - MWWorld::Ptr follower = *it; - - std::string script = follower.getClass().getScript(follower); - if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1) - continue; + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); - if ((follower.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= 800*800) - ++travellingFollowers; - } + // Add price for the travelling followers + std::set followers; + MWWorld::ActionTeleport::getFollowersToTeleport(player, followers); // Apply followers cost, in vanilla one follower travels for free - price *= std::max(1, travellingFollowers); - - price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); + price *= std::max(1, static_cast(followers.size())); MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); toAdd->setEnabled(price <= playerGold); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index ab1c0afc6..5162cac66 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -20,22 +20,12 @@ namespace MWWorld { if (mTeleportFollowers) { - //find any NPC that is following the actor and teleport him too + // Find any NPCs that are following the actor and teleport them with him std::set followers; - MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers); + getFollowersToTeleport(actor, followers); - for(std::set::iterator it = followers.begin();it != followers.end();++it) - { - MWWorld::Ptr follower = *it; - - std::string script = follower.getClass().getScript(follower); - if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1) - continue; - - if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() - <= 800*800) - teleport(*it); - } + for (std::set::iterator it = followers.begin(); it != followers.end(); ++it) + teleport(*it); } teleport(actor); @@ -66,4 +56,21 @@ namespace MWWorld world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); } } + + void ActionTeleport::getFollowersToTeleport(const MWWorld::Ptr& actor, std::set& out) { + std::set followers; + MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers); + + for(std::set::iterator it = followers.begin();it != followers.end();++it) + { + MWWorld::Ptr follower = *it; + + std::string script = follower.getClass().getScript(follower); + if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1) + continue; + + if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() <= 800*800) + out.insert(follower); + } + } } diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index 6191ee9f6..ab28d2c96 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -1,6 +1,7 @@ #ifndef GAME_MWWORLD_ACTIONTELEPORT_H #define GAME_MWWORLD_ACTIONTELEPORT_H +#include #include #include @@ -23,9 +24,12 @@ namespace MWWorld public: - ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers); ///< If cellName is empty, an exterior cell is assumed. /// @param teleportFollowers Whether to teleport any following actors of the target actor as well. + ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers); + + /// Outputs every actor follower who is in teleport range and wasn't ordered to not enter interiors + static void getFollowersToTeleport(const MWWorld::Ptr& actor, std::set& out); }; } From e58de5e410549e9da9cd30989ba0d8f03a019224 Mon Sep 17 00:00:00 2001 From: logzero Date: Sat, 24 Dec 2016 12:29:09 +0100 Subject: [PATCH 22/46] Remove superfluous velocity reflection in movement solver. The slide projection negates the reflection effect. Just to be sure I've compared the resulting vectors with and without reflection at runtime. --- apps/openmw/mwphysics/physicssystem.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 494ff681c..ab7117a10 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -366,10 +366,7 @@ namespace MWPhysics newPosition.z() <= swimlevel) { const osg::Vec3f down(0,0,-1); - float movelen = velocity.normalize(); - osg::Vec3f reflectdir = reflect(velocity, down); - reflectdir.normalize(); - velocity = slide(reflectdir, down)*movelen; + velocity = slide(velocity, down); // NOTE: remainingTime is unchanged before the loop continues continue; // velocity updated, calculate nextpos again } @@ -413,12 +410,7 @@ namespace MWPhysics else { // Can't move this way, try to find another spot along the plane - osg::Vec3f direction = velocity; - float movelen = direction.normalize(); - osg::Vec3f reflectdir = reflect(velocity, tracer.mPlaneNormal); - reflectdir.normalize(); - - osg::Vec3f newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + osg::Vec3f newVelocity = slide(velocity, tracer.mPlaneNormal); // Do not allow sliding upward if there is gravity. // Stepping will have taken care of that. From ab1724d3db1db59b789171d6b013639fcc195c8b Mon Sep 17 00:00:00 2001 From: logzero Date: Sat, 24 Dec 2016 12:38:23 +0100 Subject: [PATCH 23/46] Compare new velocity to the original velocity. Using old velocity seems awkward, probably a copypaste/refactoring bug. --- apps/openmw/mwphysics/physicssystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index ab7117a10..14e05f101 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -419,7 +419,7 @@ namespace MWPhysics if ((newVelocity-velocity).length2() < 0.01) break; - if ((velocity * origVelocity) <= 0.f) + if ((newVelocity * origVelocity) <= 0.f) break; // ^ dot product velocity = newVelocity; From a0990e589976776214cc93dd64e56de9c691d678 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 24 Dec 2016 16:05:24 +0100 Subject: [PATCH 24/46] [macOS] Switch CI to Xcode 8.2 --- .travis.yml | 2 +- CI/before_script.osx.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3e98b1f96..a669700c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ os: - linux - osx -osx_image: xcode7.3 +osx_image: xcode8.2 language: cpp sudo: required dist: trusty diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 6955825a4..f3d0f716b 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -12,7 +12,7 @@ cd build cmake \ -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ -D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \ --D CMAKE_OSX_SYSROOT="macosx10.11" \ +-D CMAKE_OSX_SYSROOT="macosx10.12" \ -D CMAKE_BUILD_TYPE=Debug \ -D OPENMW_OSX_DEPLOYMENT=TRUE \ -D DESIRED_QT_VERSION=5 \ From c2b491cd7097faf0464322150533f04ca5a0049a Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sat, 24 Dec 2016 18:30:39 +0100 Subject: [PATCH 25/46] ESSImporter: Convert last known exterior cell (Fixes #3693) + some research --- apps/essimporter/convertplayer.cpp | 8 ++++++++ apps/essimporter/importercontext.hpp | 5 ++++- apps/essimporter/importplayer.cpp | 23 +++++++++++++++++------ apps/essimporter/importplayer.hpp | 9 +++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index c363185ee..4a4a9a573 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -75,6 +75,14 @@ namespace ESSImport out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f; out.mMarkedPosition.rot[2] = mark.mRotZ; } + + if (pcdt.mHasENAM) + { + const int cellSize = 8192; + out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize; + out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize; + out.mLastKnownExteriorPosition[2] = 0.0f; + } } } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 2288b149c..6921cce92 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -62,7 +62,10 @@ namespace ESSImport playerCellId.mPaged = true; playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; mPlayer.mCellId = playerCellId; - //mPlayer.mLastKnownExteriorPosition + mPlayer.mLastKnownExteriorPosition[0] + = mPlayer.mLastKnownExteriorPosition[1] + = mPlayer.mLastKnownExteriorPosition[2] + = 0.0f; mPlayer.mHasMark = 0; mPlayer.mCurrentCrimeId = 0; // TODO mPlayer.mObject.blank(); diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index 85a3c3fd5..8c275a286 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -37,6 +37,14 @@ namespace ESSImport if (esm.isNextSub("NAM9")) esm.skipHSub(); + // Rest state. You shouldn't even be able to save during rest, but skip just in case. + if (esm.isNextSub("RNAM")) + /* + int hoursLeft; + float x, y, z; // resting position + */ + esm.skipHSub(); // 16 bytes + mBounty = 0; esm.getHNOT(mBounty, "CNAM"); @@ -70,12 +78,19 @@ namespace ESSImport mFactions.push_back(fnam); } - if (esm.isNextSub("AADT")) - esm.skipHSub(); // 44 bytes, no clue + mHasAADT = false; + if (esm.isNextSub("AADT")) // Attack animation data? + { + mHasAADT = true; + esm.getHT(mAADT); + } if (esm.isNextSub("KNAM")) esm.skipHSub(); // assigned Quick Keys, I think + if (esm.isNextSub("ANIS")) + esm.skipHSub(); // 16 bytes + if (esm.isNextSub("WERE")) { // some werewolf data, 152 bytes @@ -83,10 +98,6 @@ namespace ESSImport esm.getSubHeader(); esm.skip(152); } - - // unsure if before or after WERE - if (esm.isNextSub("ANIS")) - esm.skipHSub(); } } diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 775994444..924522383 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -98,6 +98,12 @@ struct PCDT int mCellX; int mCellY; }; + + struct AADT // 44 bytes + { + int animGroupIndex; // See convertANIS() for the mapping. + unsigned char mUnknown5[40]; + }; #pragma pack(pop) std::vector mFactions; @@ -109,6 +115,9 @@ struct PCDT bool mHasENAM; ENAM mENAM; // last exterior cell + bool mHasAADT; + AADT mAADT; + void load(ESM::ESMReader& esm); }; From 00f3bfba27d0d509c3991ec2450a80ca3c42ff35 Mon Sep 17 00:00:00 2001 From: logzero Date: Sat, 24 Dec 2016 23:07:44 +0100 Subject: [PATCH 26/46] Use tracer hit height to skip stepping up in movement solver. --- apps/openmw/mwphysics/physicssystem.cpp | 12 +++++++++--- apps/openmw/mwphysics/trace.cpp | 2 ++ apps/openmw/mwphysics/trace.h | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 14e05f101..2a3cb6137 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -396,10 +396,16 @@ namespace MWPhysics } + // We hit something. Check if we can step up. + float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z(); osg::Vec3f oldPosition = newPosition; - // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) - // NOTE: stepMove modifies newPosition if successful - bool result = stepper.step(newPosition, velocity*remainingTime, remainingTime); + bool result = false; + if (hitHeight < sStepSizeUp) + { + // Try to step up onto it. + // NOTE: stepMove does not allow stepping over, modifies newPosition if successful + result = stepper.step(newPosition, velocity*remainingTime, remainingTime); + } if (result) { // don't let pure water creatures move out of water after stepMove diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index 420ca1a9e..feda68ca5 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -78,6 +78,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star mFraction = newTraceCallback.m_closestHitFraction; mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); mEndPos = (end-start)*mFraction + start; + mHitPoint = toOsg(newTraceCallback.m_hitPointWorld); mHitObject = newTraceCallback.m_hitCollisionObject; } else @@ -85,6 +86,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star mEndPos = end; mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); mFraction = 1.0f; + mHitPoint = end; mHitObject = NULL; } } diff --git a/apps/openmw/mwphysics/trace.h b/apps/openmw/mwphysics/trace.h index 7b7d0391e..0297c9e07 100644 --- a/apps/openmw/mwphysics/trace.h +++ b/apps/openmw/mwphysics/trace.h @@ -15,6 +15,7 @@ namespace MWPhysics { osg::Vec3f mEndPos; osg::Vec3f mPlaneNormal; + osg::Vec3f mHitPoint; const btCollisionObject* mHitObject; float mFraction; From a5360483bbbef3581383d60918f7b3eb754d3e04 Mon Sep 17 00:00:00 2001 From: logzero Date: Sun, 25 Dec 2016 15:34:43 +0100 Subject: [PATCH 27/46] Back off slightly when we are touching something. This can reduce the amount of movement solver failures significantly. I've observed a drop of 8 iteration cases by almost factor of ten. --- apps/openmw/mwphysics/physicssystem.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 2a3cb6137..db72ac636 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -395,6 +395,13 @@ namespace MWPhysics break; } + // We are touching something. + if (tracer.mFraction < 1E-9f) + { + // Try to separate by backing off slighly to unstuck the solver + const osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-3f; + newPosition += backOff; + } // We hit something. Check if we can step up. float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z(); From 588442b6ccf24e7ff731c32249e28949666205e7 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 25 Dec 2016 20:14:21 +0900 Subject: [PATCH 28/46] Make enemies start combat with player followers Recreates vanilla behavior of enemies starting combat with player followers and escorters. (Fixes #3691) --- apps/openmw/mwmechanics/actors.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 23a6f497f..8c1a88e3f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -310,13 +310,14 @@ namespace MWMechanics if (!actor1.getClass().isMobile(actor1)) return; + const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); bool aggressive; - if (againstPlayer) + if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) { - // followers with high fight should not engage in combat with the player (e.g. bm_bear_black_summon) - const std::list& followers = getActorsSidingWith(actor2); - if (std::find(followers.begin(), followers.end(), actor1) != followers.end()) + // Player followers and escorters with high fight should not initiate combat with the player or with + // other player followers or escorters + if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) return; aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); @@ -369,7 +370,8 @@ namespace MWMechanics { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); - if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); + if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); if (LOS) { From e8c7ad2f4b2c29d7bbd8769c6d304572700462f7 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 25 Dec 2016 23:31:44 +0900 Subject: [PATCH 29/46] Change environment check to canFight check Instead of just checking that combatants are in compatible environments, allow combat if in attack range using canFight. Together with previous commit, fixes #3690. --- apps/openmw/mwmechanics/actors.cpp | 59 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8c1a88e3f..2f1c55a2a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -29,6 +29,7 @@ #include "movement.hpp" #include "character.hpp" #include "aicombat.hpp" +#include "aicombataction.hpp" #include "aifollow.hpp" #include "aipursue.hpp" #include "actor.hpp" @@ -298,41 +299,12 @@ namespace MWMechanics if (sqrDist > sqrAiProcessingDistance) return; - // pure water creatures won't try to fight with the target on the ground - // except that creature is already hostile - if ((againstPlayer || !creatureStats.getAiSequence().isInCombat()) - && !MWMechanics::isEnvironmentCompatible(actor1, actor2)) // creature can't swim to target - { - return; - } - // no combat for totally static creatures (they have no movement or attack animations anyway) if (!actor1.getClass().isMobile(actor1)) return; const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); - bool aggressive; - - if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) - { - // Player followers and escorters with high fight should not initiate combat with the player or with - // other player followers or escorters - if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) - return; - - aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); - } - else - { - aggressive = false; - - // Make guards fight aggressive creatures - if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) - { - if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) - aggressive = true; - } - } + bool aggressive = false; // start combat if target actor is in combat with one of our followers const std::list& followers = getActorsSidingWith(actor1); @@ -366,6 +338,33 @@ namespace MWMechanics aggressive = true; } + // pure water creatures won't try to fight with the target on the ground + // except that creature is already hostile + if (!aggressive && (againstPlayer || !creatureStats.getAiSequence().isInCombat()) + && !MWMechanics::canFight(actor1,actor2)) // creature can't swim to target + { + return; + } + + if (!aggressive && againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + { + // Player followers and escorters with high fight should not initiate combat with the player or with + // other player followers or escorters + if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) + return; + + aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); + } + else + { + // Make guards fight aggressive creatures + if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) + { + if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) + aggressive = true; + } + } + if(aggressive) { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); From e10c4d8814a043a83c6b569c544b41f012407667 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 00:18:52 +0900 Subject: [PATCH 30/46] Stop combat between AI when canFight is false --- apps/openmw/mwmechanics/aicombat.cpp | 18 ++++++++++++------ apps/openmw/mwmechanics/aicombat.hpp | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index d8dae8b79..fbd3819e2 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -19,6 +19,7 @@ #include "aicombataction.hpp" #include "combat.hpp" #include "coordinateconverter.hpp" +#include "actorutil.hpp" namespace { @@ -210,13 +211,14 @@ namespace MWMechanics else { timerReact = 0; - attack(actor, target, storage, characterController); + if (attack(actor, target, storage, characterController)) + return true; } return false; } - void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) + bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) { const MWWorld::CellStore*& currentCell = storage.mCell; bool cellChange = currentCell && (actor.getCell() != currentCell); @@ -231,7 +233,10 @@ namespace MWMechanics storage.stopAttack(); characterController.setAttackingOrSpell(false); storage.mActionCooldown = 0.f; - forceFlee = true; + if (target == MWMechanics::getPlayer()) + forceFlee = true; + else + return true; } const MWWorld::Class& actorClass = actor.getClass(); @@ -243,7 +248,7 @@ namespace MWMechanics if (!forceFlee) { if (actionCooldown > 0) - return; + return false; if (characterController.readyToPrepareAttack()) { @@ -258,7 +263,7 @@ namespace MWMechanics } if (!currentAction) - return; + return false; if (storage.isFleeing() != currentAction->isFleeing()) { @@ -266,7 +271,7 @@ namespace MWMechanics { storage.startFleeing(); MWBase::Environment::get().getDialogueManager()->say(actor, "flee"); - return; + return false; } else storage.stopFleeing(); @@ -311,6 +316,7 @@ namespace MWMechanics storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated } } + return false; } void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index a2e995cb3..fbe864ca0 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -59,7 +59,8 @@ namespace MWMechanics int mTargetActorId; - void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); + /// Returns true if combat should end + bool 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); From 5a6ea4e84e37349223aaf813b5311f3fbb7175ea Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 02:03:17 +0900 Subject: [PATCH 31/46] Cleanup --- apps/openmw/mwmechanics/actors.cpp | 47 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2f1c55a2a..d2d07cf28 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -287,10 +287,12 @@ namespace MWMechanics void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) { - CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1); + const CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1); + if (creatureStats1.getAiSequence().isInCombat(actor2)) + return; - if (actor2.getClass().getCreatureStats(actor2).isDead() - || actor1.getClass().getCreatureStats(actor1).isDead()) + const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); + if (creatureStats1.isDead() || creatureStats2.isDead()) return; const ESM::Position& actor1Pos = actor1.getRefData().getPosition(); @@ -299,27 +301,25 @@ namespace MWMechanics if (sqrDist > sqrAiProcessingDistance) return; - // no combat for totally static creatures (they have no movement or attack animations anyway) + // No combat for totally static creatures if (!actor1.getClass().isMobile(actor1)) return; - const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); bool aggressive = false; - // start combat if target actor is in combat with one of our followers - const std::list& followers = getActorsSidingWith(actor1); - const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); - for (std::list::const_iterator it = followers.begin(); it != followers.end(); ++it) + // Start combat if target actor is in combat with one of our followers or escorters + const std::list& followersAndEscorters = getActorsSidingWith(actor1); + for (std::list::const_iterator it = followersAndEscorters.begin(); it != followersAndEscorters.end(); ++it) { - // need to check both ways since player doesn't use AI packages + // Need to check both ways since player doesn't use AI packages if ((creatureStats2.getAiSequence().isInCombat(*it) || it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) - && !creatureStats.getAiSequence().isInCombat(*it)) + && !creatureStats1.getAiSequence().isInCombat(*it)) aggressive = true; } - // start combat if target actor is in combat with someone we are following - for (std::list::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ++it) + // Start combat if target actor is in combat with someone we are following through a follow package + for (std::list::const_iterator it = creatureStats1.getAiSequence().begin(); it != creatureStats1.getAiSequence().end(); ++it) { if (!(*it)->sideWithTarget()) continue; @@ -329,26 +329,23 @@ namespace MWMechanics if (followTarget.isEmpty()) continue; - if (creatureStats.getAiSequence().isInCombat(followTarget)) + if (creatureStats1.getAiSequence().isInCombat(followTarget)) continue; - // need to check both ways since player doesn't use AI packages + // Need to check both ways since player doesn't use AI packages if (creatureStats2.getAiSequence().isInCombat(followTarget) || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) aggressive = true; } - // pure water creatures won't try to fight with the target on the ground - // except that creature is already hostile - if (!aggressive && (againstPlayer || !creatureStats.getAiSequence().isInCombat()) - && !MWMechanics::canFight(actor1,actor2)) // creature can't swim to target - { + // Otherwise, don't initiate combat with an unreachable target + if (!aggressive && !MWMechanics::canFight(actor1,actor2)) return; - } - if (!aggressive && againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); + if (!aggressive && (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())) { - // Player followers and escorters with high fight should not initiate combat with the player or with + // Player followers and escorters with high fight should not initiate combat here with the player or with // other player followers or escorters if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) return; @@ -360,12 +357,12 @@ namespace MWMechanics // Make guards fight aggressive creatures if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) { - if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) + if (creatureStats1.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) aggressive = true; } } - if(aggressive) + if (aggressive) { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); From 6fa0354a179b110e2bc12e465dab51e93feaeeae Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 04:50:22 +0900 Subject: [PATCH 32/46] Make AI attack player also if it attacks follower --- apps/openmw/mwmechanics/actors.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d2d07cf28..441e7d0f4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -338,11 +338,24 @@ namespace MWMechanics aggressive = true; } + // Initiate combat with the player if we are already in combat with a player follower or escorter + const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); + if (!aggressive && againstPlayer) + { + for (std::list::const_iterator it = playerFollowersAndEscorters.begin(); it != playerFollowersAndEscorters.end(); ++it) + { + if (creatureStats1.getAiSequence().isInCombat(*it)) + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + return; + } + } + } + // Otherwise, don't initiate combat with an unreachable target if (!aggressive && !MWMechanics::canFight(actor1,actor2)) return; - const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); if (!aggressive && (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())) { // Player followers and escorters with high fight should not initiate combat here with the player or with From 54fa921dad66844127f701569a53ce7442f72220 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 05:11:06 +0900 Subject: [PATCH 33/46] Change some AI combat engagements to not need LOS --- apps/openmw/mwmechanics/actors.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 441e7d0f4..497ec7384 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -305,8 +305,6 @@ namespace MWMechanics if (!actor1.getClass().isMobile(actor1)) return; - bool aggressive = false; - // Start combat if target actor is in combat with one of our followers or escorters const std::list& followersAndEscorters = getActorsSidingWith(actor1); for (std::list::const_iterator it = followersAndEscorters.begin(); it != followersAndEscorters.end(); ++it) @@ -315,7 +313,10 @@ namespace MWMechanics if ((creatureStats2.getAiSequence().isInCombat(*it) || it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) && !creatureStats1.getAiSequence().isInCombat(*it)) - aggressive = true; + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + return; + } } // Start combat if target actor is in combat with someone we are following through a follow package @@ -335,12 +336,15 @@ namespace MWMechanics // Need to check both ways since player doesn't use AI packages if (creatureStats2.getAiSequence().isInCombat(followTarget) || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) - aggressive = true; + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + return; + } } - - // Initiate combat with the player if we are already in combat with a player follower or escorter + + // Start combat with the player if we are already in combat with a player follower or escorter const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); - if (!aggressive && againstPlayer) + if (againstPlayer) { for (std::list::const_iterator it = playerFollowersAndEscorters.begin(); it != playerFollowersAndEscorters.end(); ++it) { @@ -353,10 +357,12 @@ namespace MWMechanics } // Otherwise, don't initiate combat with an unreachable target - if (!aggressive && !MWMechanics::canFight(actor1,actor2)) + if (!MWMechanics::canFight(actor1,actor2)) return; - if (!aggressive && (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())) + bool aggressive = false; + + if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) { // Player followers and escorters with high fight should not initiate combat here with the player or with // other player followers or escorters From 8568cd049f05df05c2df8f2215c6888ec28529d1 Mon Sep 17 00:00:00 2001 From: NeveHanter Date: Mon, 26 Dec 2016 21:46:43 +0100 Subject: [PATCH 34/46] Removed "less" character from the documentation by the requested opportunity. --- apps/openmw/mwworld/actionteleport.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index ab28d2c96..c58218750 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -24,7 +24,7 @@ namespace MWWorld public: - ///< If cellName is empty, an exterior cell is assumed. + /// If cellName is empty, an exterior cell is assumed. /// @param teleportFollowers Whether to teleport any following actors of the target actor as well. ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers); From e9d8ff532f2a37564e6a9900f6d05f223383b1ab Mon Sep 17 00:00:00 2001 From: Jules Blok Date: Thu, 29 Dec 2016 01:02:21 +0100 Subject: [PATCH 35/46] OpenAL_Output: When a source is finished, rewind it instead of stopping it. This works around a bug in the MacOS OpenAL implementation. --- apps/openmw/mwsound/openal_output.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index f94cf9b43..fb259ff5f 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -437,7 +437,9 @@ bool OpenAL_SoundStream::process() alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state != AL_PLAYING && state != AL_PAUSED) { + // Ensure all processed buffers are removed so we don't replay them. refillQueue(); + alSourcePlay(mSource); } } @@ -906,7 +908,10 @@ void OpenAL_Output::finishSound(MWBase::SoundPtr sound) ALuint source = GET_PTRID(sound->mHandle); sound->mHandle = 0; - alSourceStop(source); + // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, + // which works around a bug in the MacOS OpenAL implementation which would otherwise think + // the initial queue already played when it hasn't. + alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); mFreeSources.push_back(source); @@ -1006,7 +1011,10 @@ void OpenAL_Output::finishStream(MWBase::SoundStreamPtr sound) sound->mHandle = 0; mStreamThread->remove(stream); - alSourceStop(source); + // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, + // which works around a bug in the MacOS OpenAL implementation which would otherwise think + // the initial queue already played when it hasn't. + alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); mFreeSources.push_back(source); From dbf0fa6766f580556cb69503f1ee6f60057790e7 Mon Sep 17 00:00:00 2001 From: logzero Date: Sat, 31 Dec 2016 10:57:06 +0100 Subject: [PATCH 36/46] Skip stepping if movement tracer hits actor. --- apps/openmw/mwphysics/physicssystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index db72ac636..87a7ab3e5 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -407,7 +407,7 @@ namespace MWPhysics float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z(); osg::Vec3f oldPosition = newPosition; bool result = false; - if (hitHeight < sStepSizeUp) + if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject)) { // Try to step up onto it. // NOTE: stepMove does not allow stepping over, modifies newPosition if successful From 97d7b1a3b89f6e756fc32e18664196dd1d634863 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 1 Jan 2017 20:20:02 +0100 Subject: [PATCH 37/46] Update AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 185b5ee66..93b85cb59 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -71,6 +71,7 @@ Programmers John Blomberg (fstp) Jordan Ayers Jordan Milne + Jules Blok (Armada651) Julien Voisin (jvoisin/ap0) Karl-Felix Glatzer (k1ll) Kevin Poitra (PuppyKevin) From 911807ad4f2fcf221f749b5549b012a983fbca22 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 1 Jan 2017 20:45:37 +0100 Subject: [PATCH 38/46] Update AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 93b85cb59..190a2f345 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -81,6 +81,7 @@ Programmers lazydev Leon Krieg (lkrieg) Leon Saunders (emoose) + logzero lohikaarme Lukasz Gromanowski (lgro) Manuel Edelmann (vorenon) From 7b5f3e3cdc6b5650ea0a6bf6567a40babc39ddee Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 1 Jan 2017 21:34:31 +0100 Subject: [PATCH 39/46] Fix crash in ProjectileManager when a sound id fails to play or is not found --- apps/openmw/mwworld/projectilemanager.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 4d342336d..150d70c93 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -280,7 +280,9 @@ namespace MWWorld MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); for (size_t it = 0; it != state.mSoundIds.size(); it++) { - state.mSounds.push_back(sndMgr->playSound3D(pos, state.mSoundIds.at(it), 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop)); + MWBase::SoundPtr sound = sndMgr->playSound3D(pos, state.mSoundIds.at(it), 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + if (sound) + state.mSounds.push_back(sound); } mMagicBolts.push_back(state); @@ -571,8 +573,10 @@ namespace MWWorld for (size_t soundIter = 0; soundIter != state.mSoundIds.size(); soundIter++) { - state.mSounds.push_back(sndMgr->playSound3D(esm.mPosition, state.mSoundIds.at(soundIter), 1.0f, 1.0f, - MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop)); + MWBase::SoundPtr sound = sndMgr->playSound3D(esm.mPosition, state.mSoundIds.at(soundIter), 1.0f, 1.0f, + MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + if (sound) + state.mSounds.push_back(sound); } mMagicBolts.push_back(state); From 48a23d61b26192b54cecbc2939aac6999a7d7542 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 2 Jan 2017 04:01:04 +0100 Subject: [PATCH 40/46] Mask out water in global map overlay --- apps/openmw/mwrender/globalmap.cpp | 32 ++++++++++++++++++++++++++++++ apps/openmw/mwrender/globalmap.hpp | 1 + 2 files changed, 33 insertions(+) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 6663b8b29..eae63c514 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -144,6 +145,10 @@ namespace MWRender image->allocateImage(mWidth, mHeight, 1, GL_RGB, GL_UNSIGNED_BYTE); unsigned char* data = image->data(); + osg::ref_ptr alphaImage = new osg::Image; + alphaImage->allocateImage(mWidth, mHeight, 1, GL_ALPHA, GL_UNSIGNED_BYTE); + unsigned char* alphaData = alphaImage->data(); + for (int x = mMinX; x <= mMaxX; ++x) { for (int y = mMinY; y <= mMaxY; ++y) @@ -208,6 +213,8 @@ namespace MWRender data[texelY * mWidth * 3 + texelX * 3] = r; data[texelY * mWidth * 3 + texelX * 3+1] = g; data[texelY * mWidth * 3 + texelX * 3+2] = b; + + alphaData[texelY * mWidth+ texelX] = (y2 < 0) ? static_cast(0) : static_cast(255); } } loadingListener->increaseProgress(); @@ -224,6 +231,14 @@ namespace MWRender mBaseTexture->setImage(image); mBaseTexture->setResizeNonPowerOfTwoHint(false); + mAlphaTexture = new osg::Texture2D; + mAlphaTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mAlphaTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + mAlphaTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + mAlphaTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + mAlphaTexture->setImage(alphaImage); + mAlphaTexture->setResizeNonPowerOfTwoHint(false); + clear(); loadingListener->loadingOff(); @@ -299,6 +314,23 @@ namespace MWRender stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + + if (mAlphaTexture) + { + osg::ref_ptr texcoords = new osg::Vec2Array; + texcoords->push_back(osg::Vec2f(0.f, 0.f)); + texcoords->push_back(osg::Vec2f(0.f, 1.f)); + texcoords->push_back(osg::Vec2f(1.f, 1.f)); + texcoords->push_back(osg::Vec2f(1.f, 0.f)); + geom->setTexCoordArray(1, texcoords, osg::Array::BIND_PER_VERTEX); + + stateset->setTextureAttributeAndModes(1, mAlphaTexture, osg::StateAttribute::ON); + osg::ref_ptr texEnvCombine = new osg::TexEnvCombine; + texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE); + texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + stateset->setTextureAttributeAndModes(1, texEnvCombine); + } + camera->addChild(geom); } diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index df8aa9962..1c44439fd 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -107,6 +107,7 @@ namespace MWRender std::vector< std::pair > mExploredCells; osg::ref_ptr mBaseTexture; + osg::ref_ptr mAlphaTexture; // GPU copy of overlay // Note, uploads are pushed through a Camera, instead of through mOverlayImage From 80c008906b8d072080d0afb2ebe84d5958f98fdf Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 2 Jan 2017 04:50:33 +0100 Subject: [PATCH 41/46] Fix texture coordinates --- apps/openmw/mwrender/globalmap.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index eae63c514..5cf1ecd36 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -318,10 +318,15 @@ namespace MWRender if (mAlphaTexture) { osg::ref_ptr texcoords = new osg::Vec2Array; - texcoords->push_back(osg::Vec2f(0.f, 0.f)); - texcoords->push_back(osg::Vec2f(0.f, 1.f)); - texcoords->push_back(osg::Vec2f(1.f, 1.f)); - texcoords->push_back(osg::Vec2f(1.f, 0.f)); + + float x1 = x / static_cast(mWidth); + float x2 = (x + width) / static_cast(mWidth); + float y1 = y / static_cast(mHeight); + float y2 = (y + height) / static_cast(mHeight); + texcoords->push_back(osg::Vec2f(x1, y1)); + texcoords->push_back(osg::Vec2f(x1, y2)); + texcoords->push_back(osg::Vec2f(x2, y2)); + texcoords->push_back(osg::Vec2f(x2, y1)); geom->setTexCoordArray(1, texcoords, osg::Array::BIND_PER_VERTEX); stateset->setTextureAttributeAndModes(1, mAlphaTexture, osg::StateAttribute::ON); From eee49b7ea79441cf576e1b9a4078bb2afa2e6de0 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 2 Jan 2017 17:35:50 +0900 Subject: [PATCH 42/46] Make dispel an instant effect again (Fixes #3695) --- apps/openmw/mwmechanics/spellcasting.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 43d77b99d..1d1d8cf25 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -613,6 +613,14 @@ namespace MWMechanics return true; } } + else if (target.getClass().isActor()) + { + if (effectId == ESM::MagicEffect::Dispel) + { + target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); + return true; + } + } else if (target.getClass().isActor() && target == getPlayer()) { MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mCaster); @@ -1140,9 +1148,6 @@ namespace MWMechanics case ESM::MagicEffect::CureCorprusDisease: actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease(); break; - case ESM::MagicEffect::Dispel: - actor.getClass().getCreatureStats(actor).getActiveSpells().purgeAll(magnitude); - break; case ESM::MagicEffect::RemoveCurse: actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); break; From 1a073ca6426de17c259e7b395333bc4287bd198a Mon Sep 17 00:00:00 2001 From: Assumeru Date: Tue, 3 Jan 2017 22:02:23 +0100 Subject: [PATCH 43/46] Fix teleportation being unreachable --- apps/openmw/mwmechanics/spellcasting.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 1d1d8cf25..489ab83ec 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -613,13 +613,10 @@ namespace MWMechanics return true; } } - else if (target.getClass().isActor()) + else if (target.getClass().isActor() && effectId == ESM::MagicEffect::Dispel) { - if (effectId == ESM::MagicEffect::Dispel) - { - target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); - return true; - } + target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); + return true; } else if (target.getClass().isActor() && target == getPlayer()) { From 07423f973fe2465e4167f2eb554317a5d1f1d1dd Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 5 Jan 2017 14:02:10 +0300 Subject: [PATCH 44/46] [macOS] Use newer prebuilt dependencies on CI --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index a2ea720e5..941c10a02 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -7,5 +7,5 @@ brew rm pkgconfig || true brew rm qt5 || true brew install cmake pkgconfig $macos_qt_formula -curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip +curl http://downloads.openmw.org/osx/dependencies/openmw-deps-0ecece4.zip -o ~/openmw-deps.zip unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From 7bd95c8ce32d42078a3c10c34c0b3db9505e57f1 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 5 Jan 2017 14:06:52 +0300 Subject: [PATCH 45/46] [macOS] Use https link to download dependencies on CI --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 941c10a02..49af86a3e 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -7,5 +7,5 @@ brew rm pkgconfig || true brew rm qt5 || true brew install cmake pkgconfig $macos_qt_formula -curl http://downloads.openmw.org/osx/dependencies/openmw-deps-0ecece4.zip -o ~/openmw-deps.zip +curl https://downloads.openmw.org/osx/dependencies/openmw-deps-0ecece4.zip -o ~/openmw-deps.zip unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From 5e992a0342e83614f68f505daf68973fcfd31ebd Mon Sep 17 00:00:00 2001 From: Allofich Date: Fri, 6 Jan 2017 02:58:24 +0900 Subject: [PATCH 46/46] Fix attempting to access NPC stats on creatures --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 3102354a6..830c2bcac 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -448,14 +448,14 @@ namespace MWDialogue { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); - // Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate) - float curDisp = static_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false)); - if (curDisp + mPermanentDispositionChange < 0) - mPermanentDispositionChange = -curDisp; - // Apply disposition change to NPC's base disposition if (mActor.getClass().isNpc()) { + // Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate) + float curDisp = static_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false)); + if (curDisp + mPermanentDispositionChange < 0) + mPermanentDispositionChange = -curDisp; + MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor); npcStats.setBaseDisposition(static_cast(npcStats.getBaseDisposition() + mPermanentDispositionChange)); }