From 147ce4d267e492f2606445bba2ed62a6bd56c31d Mon Sep 17 00:00:00 2001 From: Ryan Tucker Date: Sat, 15 Oct 2016 19:03:14 -0700 Subject: [PATCH 01/77] Started settings guide and general edits --- docs/source/openmw-mods/index.rst | 3 +- docs/source/openmw-mods/mod-install.rst | 11 ++- docs/source/openmw-mods/settings/camera.rst | 75 +++++++++++++++++++++ docs/source/openmw-mods/settings/cells.rst | 17 +++++ docs/source/openmw-mods/settings/index.rst | 21 ++++++ docs/source/openmw-mods/settings/map.rst | 53 +++++++++++++++ 6 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 docs/source/openmw-mods/settings/camera.rst create mode 100644 docs/source/openmw-mods/settings/cells.rst create mode 100644 docs/source/openmw-mods/settings/index.rst create mode 100644 docs/source/openmw-mods/settings/map.rst diff --git a/docs/source/openmw-mods/index.rst b/docs/source/openmw-mods/index.rst index 49113426c9..8fda12702d 100644 --- a/docs/source/openmw-mods/index.rst +++ b/docs/source/openmw-mods/index.rst @@ -13,4 +13,5 @@ The following document is the complete reference guide to modifying, or modding, foreword differences - mod-install \ No newline at end of file + mod-install + settings/index \ No newline at end of file diff --git a/docs/source/openmw-mods/mod-install.rst b/docs/source/openmw-mods/mod-install.rst index 3177699903..7a03965ec2 100644 --- a/docs/source/openmw-mods/mod-install.rst +++ b/docs/source/openmw-mods/mod-install.rst @@ -22,6 +22,13 @@ You have now installed your mod. Any simple replacer mods that only contain reso Enable ------ -Any mods that have plugin files must be enabled to work. +Any mods that have plugin files must be enabled to work. Master game files and plugin files can only be enabled if they have been properly installed within a *data folder* as described above. -#. \ No newline at end of file +#. Open the OpenMW Launcher. +#. Click on the Data Files tab. +#. In the Content List box, select the content list you wish to modify in the dropdown menu, or make a new one by: + #. Click the New Content List button and enter the name of your content list, then click OK. New lists are useful for keeping track of the mods used for different characters or for different games if you play more than one game using OpenMW. + #. In the Content box, select your game file (``.esm`` or ``.omwgame``) from the dropdown menu. +#. Now you must activate the plugins you wish to use by checking the box next to their entry in the Content box list. +#. Load order can be changed simply by dragging the entries around within the list. Mods are loaded from the top down, so if one plugin depends on another, it must be lower on the list. +#. Click Play to run OpenMW with your game and enabled mods! \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/camera.rst b/docs/source/openmw-mods/settings/camera.rst new file mode 100644 index 0000000000..796e117cb4 --- /dev/null +++ b/docs/source/openmw-mods/settings/camera.rst @@ -0,0 +1,75 @@ +Camera Settings +############### + +near clip +--------- + +:Type: floating point +:Range: > 0 +:Default: 1.0 + +This floating point setting controls the distance to the near clipping plane. The value must be greater than zero. Values greater than approximately 18.0 will occasionally clip objects in the world in front of the character. Values greater than approximately 8.0 will clip the character's hands in first person view and/or the back of their head in third person view. + +The default value is 1.0. This setting can only be configured by editing the settings configuration file. The value must be greater than 0.0, but it's unclear if the engine enforces this limitation. + +small feature culling +--------------------- + +:Type: boolean +:Range: True/False +:Default: True + +This boolean setting determines whether objects that render to a few pixels or smaller will be culled (not drawn). It generally improves performance to enable this feature, and by definition the culled objects will be very small on screen. It appears that the default definition of "small" in OpenSceneGraph is 2x2 pixels. + +The default value is true. This setting can only be configured by editing the settings configuration file. + +viewing distance +---------------- + +:Type: floating point +:Range: > 0 +:Default: 6666.0 + +This floating point values controls the maximum visible distance (also called the far clipping plane). Larger values significantly improve rendering in exterior spaces, but also increase the amount of rendered geometry and significantly reduce the frame rate. This value interacts with the exterior cell load distance setting in that it's probably undesired for this value to provide visibility into cells that have not yet been loaded. When cells are visible before loading, the geometry will "pop-in" suddenly, creating a jarring visual effect. To prevent this effect, this value must be less than:: + + (8192 * exterior cell load distance - 1024) * 0.93 + +The constant 8192 is the size of a cell, and 1024 is the threshold distance for loading a new cell. Additionally, the field of view setting also interacts with this setting because the view frustrum end is a plane, so you can see further at the edges of the screen than you should be able to. This can be observed in game by looking at distant objects and rotating the camera so the objects are near the edge of the screen. As a result, this setting should further be reduced by a factor that depends on the field of view setting. In the default configuration this reduction is 7%, hence the factor of 0.93 above. Using this factor, approximate values recommended for other exterior cell load distance settings are: + +======= ======== + Cells Viewing + Distance +======= ======== +2 14285 +3 21903 +4 29522 +5 35924 +======= ======== + +Reductions of up to 25% or more can be required to completely eliminate pop-in for wide fields of view and long viewing distances near the edges of the screen, but such situations are unusual and probably not worth the performance penalty introduced by loading geometry obscured by fog in the center of the screen. See RenderingManager::configureFog for the relevant source code. + +Enabling the distant land setting is an alternative to increasing exterior cell load distance. Note that the distant land setting does not include rendering of distant static objects, so the resulting visual effect is not the same. + +The default value is 6666.0. This setting can be adjusted in game from the ridiculously low value of 2000.0 to a maximum of 6666.0, using the View Distance slider in the Detail tab of the Video panel of the Options menu. + +field of view +------------- + +:Type: floating point +:Range: 0-360 +:Default: 55.0 + +Sets the camera field of view in degrees. Recommended values range from 30 degrees to 110 degrees. Small values provide a very narrow field of view that creates a "zoomed in" effect, while large values cause distortion at the edges of the screen. The "field of view" setting interacts with aspect ratio of your video resolution in that more square aspect ratios (e.g. 4:3) need a wider field of view to more resemble the same field of view on a widescreen (e.g. 16:9) monitor. + +The default value is 55.0. This setting can be changed in game using the Field of View slider from the Video tab of the Video panel of the Options menu. + +first person field of view +-------------------------- + +:Type: floating point +:Range: 0-360 +:Default: 55.0 + +The floating point setting controls the field of view for first person meshes such as the player's hands and held objects. It is not recommended to change this value from its default value because the Bethesda provided Morrowind assets do not adapt well to large values, while small values can result in the hands not being visible. + +The default value is 55.0. This setting can only be configured by editing the settings configuration file. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/cells.rst b/docs/source/openmw-mods/settings/cells.rst new file mode 100644 index 0000000000..3a5947ff1b --- /dev/null +++ b/docs/source/openmw-mods/settings/cells.rst @@ -0,0 +1,17 @@ +Cells Settings +############## + +exterior cell load distance +--------------------------- + +:Type: integer +:Range: >= 1 +:Default: 1 + +This integer setting determines the number of exterior cells adjacent to the character that will be loaded for rendering. Values greater than one may significantly affect loading times when exiting interior spaces or loading additional exterior cells. Caution is advised when increasing this setting. + +This setting interacts with viewing distance and field of view settings. + +It is generally very wasteful for this value to load geometry than will almost never be visible due to viewing distance and fog. For low frame rate screen shots of scenic vistas, this setting should be set high, and viewing distances adjusted accordingly. + +The default value is 1. This value must be greater than or equal to 1. This setting can only be configured by editing the settings configuration file. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/index.rst b/docs/source/openmw-mods/settings/index.rst new file mode 100644 index 0000000000..3702556ab7 --- /dev/null +++ b/docs/source/openmw-mods/settings/index.rst @@ -0,0 +1,21 @@ +############################### +Advanced Settings Configuration +############################### + +This part of the guide will cover how to make modifications to the more arcane settings in OpenMW, most of which are not available from in-game menus, to optimize or customize your OpenMW experience. If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this will be quite similar. All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory. This directory is located differently depending on your operating system, shown below: + +Windows: + ``C:\Users\\Documents\my games\openmw`` (hard drive, username and language may vary) +Mac OSX: + ``$HOME/Library/Preferences/openmw`` +Linux: + ``$HOME/.config/openmw`` + +Although this guide attempts to be comprehensive and up to date. You will always be able to find the full list of settings available and their default values in ``settings-default.cfg`` in your main OpenMW installation directory. The ranges I have included with each setting are the physically possible ranges, not recommendations. + +.. warning:: + As the title suggests, these are advanced settings. If digging around plain text files and manually editing settings sounds scary to you, you may want to stear clear of altering these files. That being said, this guide should be plenty clear enough that you can find the setting you want to change and safely edit it. + +.. toctree:: + :caption: Table of Contents + :maxdepth: 2 \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/map.rst b/docs/source/openmw-mods/settings/map.rst new file mode 100644 index 0000000000..e03bfd615b --- /dev/null +++ b/docs/source/openmw-mods/settings/map.rst @@ -0,0 +1,53 @@ +Map Settings +############ + +global map size +--------------- + +:Type: integer +:Range: >= 1 +:Default: 18 + +This integer setting adjusts the scale of the world map in the GUI mode map window. The value is the width in pixels of each cell in the map, so larger values result in larger more detailed world maps, while smaller values result in smaller less detailed world maps. However, the native resolution of the map source material appears to be 9 pixels per unexplored cell and approximately 18 pixels per explored cell, so values larger than 36 don't produce much additional detail. Similarly, the size of place markers is currently fixed at 12 pixels, so values smaller than this result in overlapping place markers. Values from 12 to 36 are recommended. For reference, Vvardenfell is approximately 41x36 cells. + +Warning: Changing this setting affects saved games. The currently explored area is stored as an image in the save file that's overlayed on the default world map in game. When you increase the resolution of the map, the overlay of earlier saved games will be scaled up on load, and appear blurry. When you visit the cell again, the overlay for that cell is regenerated at the new resolution, so the blurry areas can be corrected by revisiting all the cells you've already visited. + +The default value for this setting is 18. This setting can not be configured except by editing the settings configuration file. + +local map hud widget size +------------------------- + +:Type: integer +:Range: >= 1 +:Default: 256 + +This integer setting controls the zoom level for the HUD map widget (the map in the lower right corner of the window). A value of 64 results in the HUD map widget displaying one entire exterior cell. Since the GUI mode map displays 3x3 cells, a value of approximately 21 displays the same area as the GUI mode map. Larger values increase the level of zoom, while smaller values are wasteful since there's no map data to display beyond the 3x3 cell grid. + +Note that the actual size of the widget is always the same on the screen unless the scaling factor setting in the "GUI" section is changed. Increasing both the scaling factor of the GUI and this setting does result in a higher resolution HUD map, but unfortunately with a scaled direction pointer on top of it. + +The default value for this setting is 256. This setting can not be configured except by editing the settings configuration file. + +local map resolution +-------------------- + +:Type: integer +:Range: >= 1 +:Default: 256 + +This integer setting controls the resolution of the GUI mode local map window. Larger values generally increase the visible detail in map. If this setting is half the local map widget size or smaller, the map will generally be be fairly blurry. Setting both options to the same value results in a map with good detail. Values that exceed the local map widget size setting by more than a factor of two are unlikely to provide much of an improvement in detail since they're subsequently scaled back to the approximately the map widget size before display. The video resolution settings interacts with this setting in that regard. + +.. warning:: + Increasing this setting can increase cell load times, because the map is rendered on demand each time you enter a new cell. Large values may exceed video card limits or exhaust VRAM. + +The default value for this setting is 256. This setting can not be configured except by editing the settings configuration file. + +local map widget size +--------------------- + +:Type: integer +:Range: >= 1 +:Default: 512 + +This integer setting controls the canvas size of the GUI mode local map window. Larger values result in a larger physical map size on screen, and typically require more panning to see all available portions of the map. This larger size also enables an overall greater level of detail if the local map resolution setting is also increased. + +The default value for this setting is 512. This setting can not be configured except by editing the settings configuration file. \ No newline at end of file From 95adab9458604493bf8e3ba7cb278f7e05b2d114 Mon Sep 17 00:00:00 2001 From: rhtucker Date: Mon, 17 Oct 2016 13:06:15 -0700 Subject: [PATCH 02/77] test nested enumerated lists with autonumbers --- docs/source/openmw-mods/mod-install.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/openmw-mods/mod-install.rst b/docs/source/openmw-mods/mod-install.rst index 7a03965ec2..18b7eb952e 100644 --- a/docs/source/openmw-mods/mod-install.rst +++ b/docs/source/openmw-mods/mod-install.rst @@ -6,13 +6,13 @@ The following is a detailed guide on how to install and enable mods in OpenMW us Install ------- -#. Your mod probably comes in some kind of archive, such as ``.zip``, ``.rar``, ``.7z``, or something along those lines. Unpack this archive into its own folder. -#. Ensure the structure of this folder is correct. - #. Locate the plugin files, ``.esp`` or ``.omwaddon``. The folder containing the plugin files we will call your *data folder* - #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. +#. Your mod probably comes in some kind of archive, such as ``.zip``, ``.rar``, ``.7z``, or something along those lines. Unpack this archive into its own folder. +#. Ensure the structure of this folder is correct. + #. Locate the plugin files, ``.esp`` or ``.omwaddon``. The folder containing the plugin files we will call your *data folder* + #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. .. note:: There may be multiple levels of folders, but the location of the plugins must be the same as the resource folders. -#. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in https://wiki.openmw.org/index.php?title=Paths and *not* in your OpenMW root directory. +3. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in https://wiki.openmw.org/index.php?title=Paths and *not* in your OpenMW root directory. #. Find or search for ``data=``. This is located very near the bottom of the file. If you are using Morrowind, this first entry should already point to your Morrowind data directory, ``Data Files``; otherwise it will point to your game file, ``.omwgame``. #. Create a new line underneath and type: ``data="path/to/your/data folder"`` Remember, the *data folder* is where your mod's plugin files are. The double quotes around this path name are *required*. #. Save your ``openmw.cfg`` file. @@ -31,4 +31,4 @@ Any mods that have plugin files must be enabled to work. Master game files and p #. In the Content box, select your game file (``.esm`` or ``.omwgame``) from the dropdown menu. #. Now you must activate the plugins you wish to use by checking the box next to their entry in the Content box list. #. Load order can be changed simply by dragging the entries around within the list. Mods are loaded from the top down, so if one plugin depends on another, it must be lower on the list. -#. Click Play to run OpenMW with your game and enabled mods! \ No newline at end of file +#. Click Play to run OpenMW with your game and enabled mods! From 201a89a698f982ecb4fc031b86f27a073a41d051 Mon Sep 17 00:00:00 2001 From: rhtucker Date: Mon, 17 Oct 2016 13:32:34 -0700 Subject: [PATCH 03/77] fixed nested enumerated lists and split numbering --- docs/source/openmw-mods/mod-install.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/source/openmw-mods/mod-install.rst b/docs/source/openmw-mods/mod-install.rst index 18b7eb952e..e6ce2a7f13 100644 --- a/docs/source/openmw-mods/mod-install.rst +++ b/docs/source/openmw-mods/mod-install.rst @@ -6,10 +6,12 @@ The following is a detailed guide on how to install and enable mods in OpenMW us Install ------- -#. Your mod probably comes in some kind of archive, such as ``.zip``, ``.rar``, ``.7z``, or something along those lines. Unpack this archive into its own folder. -#. Ensure the structure of this folder is correct. - #. Locate the plugin files, ``.esp`` or ``.omwaddon``. The folder containing the plugin files we will call your *data folder* - #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. +#. Your mod probably comes in some kind of archive, such as ``.zip``, ``.rar``, ``.7z``, or something along those lines. Unpack this archive into its own folder. +#. Ensure the structure of this folder is correct. + + #. Locate the plugin files, ``.esp`` or ``.omwaddon``. The folder containing the plugin files we will call your *data folder* + #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. + .. note:: There may be multiple levels of folders, but the location of the plugins must be the same as the resource folders. 3. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in https://wiki.openmw.org/index.php?title=Paths and *not* in your OpenMW root directory. @@ -27,8 +29,10 @@ Any mods that have plugin files must be enabled to work. Master game files and p #. Open the OpenMW Launcher. #. Click on the Data Files tab. #. In the Content List box, select the content list you wish to modify in the dropdown menu, or make a new one by: + #. Click the New Content List button and enter the name of your content list, then click OK. New lists are useful for keeping track of the mods used for different characters or for different games if you play more than one game using OpenMW. #. In the Content box, select your game file (``.esm`` or ``.omwgame``) from the dropdown menu. + #. Now you must activate the plugins you wish to use by checking the box next to their entry in the Content box list. #. Load order can be changed simply by dragging the entries around within the list. Mods are loaded from the top down, so if one plugin depends on another, it must be lower on the list. #. Click Play to run OpenMW with your game and enabled mods! From b5cd8188f57ffe7660e4c2800f40d1d4a1bc6f79 Mon Sep 17 00:00:00 2001 From: rhtucker Date: Mon, 17 Oct 2016 13:33:38 -0700 Subject: [PATCH 04/77] fixed nested levels --- docs/source/openmw-mods/mod-install.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/openmw-mods/mod-install.rst b/docs/source/openmw-mods/mod-install.rst index e6ce2a7f13..85b0015bab 100644 --- a/docs/source/openmw-mods/mod-install.rst +++ b/docs/source/openmw-mods/mod-install.rst @@ -9,8 +9,8 @@ Install #. Your mod probably comes in some kind of archive, such as ``.zip``, ``.rar``, ``.7z``, or something along those lines. Unpack this archive into its own folder. #. Ensure the structure of this folder is correct. - #. Locate the plugin files, ``.esp`` or ``.omwaddon``. The folder containing the plugin files we will call your *data folder* - #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. + #. Locate the plugin files, ``.esp`` or ``.omwaddon``. The folder containing the plugin files we will call your *data folder* + #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. .. note:: There may be multiple levels of folders, but the location of the plugins must be the same as the resource folders. From cffd2bdd3075151ee18699a89d88d95e33adff97 Mon Sep 17 00:00:00 2001 From: Ryan Tucker Date: Tue, 18 Oct 2016 21:46:16 -0700 Subject: [PATCH 05/77] skeleton for full settings list --- docs/source/openmw-mods/settings/GUI.rst | 104 +++++++++++++++++++ docs/source/openmw-mods/settings/HUD.rst | 13 +++ docs/source/openmw-mods/settings/game.rst | 25 +++++ docs/source/openmw-mods/settings/general.rst | 9 ++ docs/source/openmw-mods/settings/index.rst | 16 ++- docs/source/openmw-mods/settings/input.rst | 9 ++ docs/source/openmw-mods/settings/saves.rst | 9 ++ docs/source/openmw-mods/settings/sound.rst | 9 ++ docs/source/openmw-mods/settings/video.rst | 9 ++ docs/source/openmw-mods/settings/water.rst | 9 ++ docs/source/openmw-mods/settings/windows.rst | 9 ++ 11 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 docs/source/openmw-mods/settings/GUI.rst create mode 100644 docs/source/openmw-mods/settings/HUD.rst create mode 100644 docs/source/openmw-mods/settings/game.rst create mode 100644 docs/source/openmw-mods/settings/general.rst create mode 100644 docs/source/openmw-mods/settings/input.rst create mode 100644 docs/source/openmw-mods/settings/saves.rst create mode 100644 docs/source/openmw-mods/settings/sound.rst create mode 100644 docs/source/openmw-mods/settings/video.rst create mode 100644 docs/source/openmw-mods/settings/water.rst create mode 100644 docs/source/openmw-mods/settings/windows.rst diff --git a/docs/source/openmw-mods/settings/GUI.rst b/docs/source/openmw-mods/settings/GUI.rst new file mode 100644 index 0000000000..945d660c7c --- /dev/null +++ b/docs/source/openmw-mods/settings/GUI.rst @@ -0,0 +1,104 @@ +GUI Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 + +This floating point setting scales the GUI interface windows. The value must be greater than 0.0. A value of 1.0 results in the normal scale. Values much larger than 2.0 may result in user interface components being inaccessible. Until a gamepad interface is created, increasing this setting is helpful for simulating the larger interface used in console games. + +The default value is 1.0. This setting can only be configured by editing the settings configuration file. + +menu transparency +----------------- + +:Type: floating point +:Range: 0.0 (transparent) to 1.0 (opaque) +:Default: 0.84 + +This floating point setting controls the transparency of the GUI windows. The value should be between 0.0 (transparent) and 1.0 (opaque). +The default value is 0.84. This setting can be adjusted in game with the Menu Transparency slider in the Prefs panel of the Options menu. + +tooltip delay +------------- + +:Type: floating point +:Range: > 0.0 +:Default: 0.0 + +This floating point value determines the number of seconds between when you begin hovering over an item and when its tooltip appears. This setting only affects the tooltip delay for objects under the crosshair in GUI mode windows. There does not appear to be a setting to control the tool tip delay in outside of GUI mode. + +The tooltip displays context sensitive information on the selected GUI element, such as weight, value, damage, armor rating, magical effects, and detailed description. + +The default value is 0.0. This setting can be adjusted between 0.0 and 1.0 in game with the Menu Help Delay slider in the Prefs panel of the Options menu. + +stretch menu background +----------------------- + +:Type: boolean +:Range: True/False +:Default: False + +Stretch or shrink the main menu screen, loading splash screens, introductory movie, and cut scenes to fill the specified video resolution, distorting their aspect ratio. The Bethesda provided assets have a 4:3 aspect ratio, but other assets are permitted to have other aspect ratios. If this setting is false, the assets will be centered in their correct aspect ratio, with black bars filling the remainder of the screen. + +The default value is false. This setting can only be configured by editing the settings configuration file. + +subtitles +--------- + +:Type: boolean +:Range: True/False +:Default: False + +Enable or disable subtitles for NPC spoken dialog (and some sound effects). Subtitles will appear in a tool tip box in the lower center of the screen. + +The default value is false. This setting can be toggled in game with the Subtitles button in the Prefs panel of Options menu. + +hit fader +--------- + +:Type: boolean +:Range: True/False +:Default: True + +This boolean setting enables or disables the "red flash" overlay that provides a visual clue when the character has taken damage. + +If this setting is disabled, the player will "bleed" like NPCs do. + +The default value is true. This setting can only be configured by editing the settings configuration file. + +werewolf overlay +---------------- + +:Type: boolean +:Range: True/False +:Default: True + +Enable or disable the werewolf overlay. + +The default value is true. This setting can only be configured by editing the settings configuration file. + +color background owned +---------------------- + +:Type: RGBA floating point +:Range: 0.0 to 1.0 +:Default: 0.15 0.0 0.0 1.0 + +The following two settings determine the background color of the tool tip and the crosshair when hovering over an item owned by an NPC. The color definitions are composed of four floating point values between 0.0 and 1.0 inclusive, representing the red, green, blue and alpha channels. The alpha value is currently ignored. The crosshair color will have no effect if the crosshair setting in the HUD section is disabled. + +The default value is "0.15 0.0 0.0 1.0", which is a dark red color. This setting can only be configured by editing the settings configuration file. This setting has no effect if the show owned setting in the Game Settings Section is false. + +color crosshair owned +--------------------- + +:Type: RGBA floating point +:Range: 0.0 to 1.0 +:Default: 1.0 0.15 0.15 1.0 + +This setting sets the color of the crosshair when hovering over an item owned by an NPC. The value is composed of four floating point values representing the red, green, blue and alpha channels. The alpha value is currently ignored. + +The default value is "1.0 0.15 0.15 1.0" which is a bright red color. This setting can only be configured by editing the settings configuration file. This setting has no effect if the crosshair setting in the HUD Settings Section is false. This setting has no effect if the show owned setting in the Game Settings Section is false. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/HUD.rst b/docs/source/openmw-mods/settings/HUD.rst new file mode 100644 index 0000000000..9a15e3abaf --- /dev/null +++ b/docs/source/openmw-mods/settings/HUD.rst @@ -0,0 +1,13 @@ +HUD Settings +############ + +crosshair +--------- + +:Type: boolean +:Range: True/False +:Default: True + +This boolean setting determines whether the crosshair or reticle is displayed. Some players perceive that disabling the crosshair provides a more immersive experience. Another common use is to disable the crosshair for screen shots. Enabling the crosshair provides more immediate feedback about which object that is currently the focus of actions. + +The default value is true. This setting can be toggled with the Crosshair button in the Prefs panel of the Options menu. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/game.rst b/docs/source/openmw-mods/settings/game.rst new file mode 100644 index 0000000000..1feb3f7e98 --- /dev/null +++ b/docs/source/openmw-mods/settings/game.rst @@ -0,0 +1,25 @@ +Game Settings +############ + +show owned +---------- + +:Type: integer +:Range: 0, 1, 2, 3 +:Default: 0 + +Enable visual clues for items owned by NPCs when the crosshair is on the object. If the setting is 0, no clues are provided which is the default Morrowind behavior. If the setting is 1, the background of the tool tip for the object is highlight in the color specified by the color background owned setting in the GUI Settings Section. If the setting is 2, the crosshair is the color of the color crosshair owned setting in the GUI Settings section. If the setting is 3, both the tool tip background and the crosshair are colored. The crosshair is not visible if crosshair is false. + +The default value is 0 (no clues). This setting can only be configured by editing the settings configuration file. + +best attack +----------- + +:Type: boolean +:Range: True/False +:Default: False + +If this boolean setting is true, the player character will always use the most powerful attack when striking with a weapon (chop, slash or thrust). If this setting is false, the type of attack is determined by the direction that the character is moving at the time the attack begins. + +The default value is false. This setting can be toggled with the Always Use Best Attack button in the Prefs panel of the Options menu. + diff --git a/docs/source/openmw-mods/settings/general.rst b/docs/source/openmw-mods/settings/general.rst new file mode 100644 index 0000000000..f81a33ccc2 --- /dev/null +++ b/docs/source/openmw-mods/settings/general.rst @@ -0,0 +1,9 @@ +General Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/index.rst b/docs/source/openmw-mods/settings/index.rst index 3702556ab7..86c7316da5 100644 --- a/docs/source/openmw-mods/settings/index.rst +++ b/docs/source/openmw-mods/settings/index.rst @@ -18,4 +18,18 @@ Although this guide attempts to be comprehensive and up to date. You will always .. toctree:: :caption: Table of Contents - :maxdepth: 2 \ No newline at end of file + :maxdepth: 2 + + camera + cells + map + GUI + HUD + game + general + input + saves + sound + video + water + windows \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/input.rst b/docs/source/openmw-mods/settings/input.rst new file mode 100644 index 0000000000..0ffcbc30df --- /dev/null +++ b/docs/source/openmw-mods/settings/input.rst @@ -0,0 +1,9 @@ +Input Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/saves.rst b/docs/source/openmw-mods/settings/saves.rst new file mode 100644 index 0000000000..01fe9e68d3 --- /dev/null +++ b/docs/source/openmw-mods/settings/saves.rst @@ -0,0 +1,9 @@ +Saves Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/sound.rst b/docs/source/openmw-mods/settings/sound.rst new file mode 100644 index 0000000000..8d11307f39 --- /dev/null +++ b/docs/source/openmw-mods/settings/sound.rst @@ -0,0 +1,9 @@ +Sound Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/video.rst b/docs/source/openmw-mods/settings/video.rst new file mode 100644 index 0000000000..ba345a6e99 --- /dev/null +++ b/docs/source/openmw-mods/settings/video.rst @@ -0,0 +1,9 @@ +Video Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/water.rst b/docs/source/openmw-mods/settings/water.rst new file mode 100644 index 0000000000..0d115f9436 --- /dev/null +++ b/docs/source/openmw-mods/settings/water.rst @@ -0,0 +1,9 @@ +Water Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/windows.rst b/docs/source/openmw-mods/settings/windows.rst new file mode 100644 index 0000000000..1580ac2204 --- /dev/null +++ b/docs/source/openmw-mods/settings/windows.rst @@ -0,0 +1,9 @@ +Windows Settings +############ + +scaling factor +-------------- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 \ No newline at end of file From 0874d69546bf33c7527181f9788a469213cf7207 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 24 Nov 2016 17:07:52 +0100 Subject: [PATCH 06/77] 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 0d74e8cbcb..6a74dde260 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 3a6c4a75ae..d38dfaeb2e 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 07/77] updated changelog --- CHANGELOG.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0add2e02e7..54b4bd34e5 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 08/77] [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 6a74dde260..d19745ff72 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 eadec64d0a..96cadc8a79 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 acf3cf0217..e7e19be942 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 c6fe348356..fc5e8fc7a3 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 452629fcfd..ce859bc3e0 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 c861a4ac89..e3624742a0 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 2371419605..b49b72e46a 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 7494d3ba2d..6592de56e3 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 0738b5783c..5ca0d1e832 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 09/77] [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 cfe7fe3058..43901d579d 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 10/77] 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 cc3ba40e1d..fece18d20c 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 11/77] 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 1a6a62fc3b..2a5995e0c2 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 5c5fc38996..f064721030 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 852ae79dc5..6e25acf50f 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 12/77] 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 80b343d4f4..21eaa0de5f 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 3f2bde7761..a2e995cb38 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 2f66b91ac50b34c8e3b26f2a14f88a6d9a5cf81b Mon Sep 17 00:00:00 2001 From: Aussiemon Date: Fri, 16 Dec 2016 15:18:28 -0700 Subject: [PATCH 13/77] Added check to prevent attempted wandering of empty paths --- apps/openmw/mwmechanics/aiwander.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e67c733f87..73ddce4b48 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -473,8 +473,8 @@ namespace MWMechanics void AiWander::onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos) { - // Are we there yet? - if (pathTo(actor, mPathFinder.getPath().back(), duration, DESTINATION_TOLERANCE)) + // Is there no destination or are we there yet? + if ((!mPathFinder.isPathConstructed()) || pathTo(actor, mPathFinder.getPath().back(), duration, DESTINATION_TOLERANCE)) { stopWalking(actor, storage); storage.setState(Wander_ChooseAction); From eae35af13d17d84efe4ed6c7e7812dadd71d1330 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Dec 2016 00:23:22 +0100 Subject: [PATCH 14/77] Check if the bounding box changed before calling dirtyBound() --- components/sceneutil/riggeometry.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 297dc69230..92780bfe98 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -320,11 +320,14 @@ void RigGeometry::updateBounds(osg::NodeVisitor *nv) box.expandBy(bs); } - _boundingBox = box; - _boundingSphere = osg::BoundingSphere(_boundingBox); - _boundingSphereComputed = true; - for (unsigned int i=0; idirtyBound(); + if (box != _boundingBox) + { + _boundingBox = box; + _boundingSphere = osg::BoundingSphere(_boundingBox); + _boundingSphereComputed = true; + for (unsigned int i=0; idirtyBound(); + } } void RigGeometry::updateGeomToSkelMatrix(const osg::NodePath& nodePath) From c2d6e074c2201a2f8c674f218d075868b3928ff1 Mon Sep 17 00:00:00 2001 From: Leon Krieg Date: Sat, 17 Dec 2016 11:52:17 +0100 Subject: [PATCH 15/77] Handle SDL event 0x304 by doing nothing (#3670) --- components/sdlutil/sdlinputwrapper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 6482f378e2..2f0419d3a9 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -108,6 +108,8 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v case SDL_TEXTINPUT: mKeyboardListener->textInput(evt.text); break; + case SDL_KEYMAPCHANGED: + break; case SDL_JOYHATMOTION: //As we manage everything with GameController, don't even bother with these. case SDL_JOYAXISMOTION: case SDL_JOYBUTTONDOWN: From 63e093bcd0702783503d0c6d61605dda46cc1b50 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Dec 2016 12:04:03 +0100 Subject: [PATCH 16/77] Revert "Handle SDL event 0x304 by doing nothing (#3670)" --- components/sdlutil/sdlinputwrapper.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 2f0419d3a9..6482f378e2 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -108,8 +108,6 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v case SDL_TEXTINPUT: mKeyboardListener->textInput(evt.text); break; - case SDL_KEYMAPCHANGED: - break; case SDL_JOYHATMOTION: //As we manage everything with GameController, don't even bother with these. case SDL_JOYAXISMOTION: case SDL_JOYBUTTONDOWN: From 170e723cc77ec0234cbca87f259850f980040764 Mon Sep 17 00:00:00 2001 From: Leon Krieg Date: Sat, 17 Dec 2016 11:52:17 +0100 Subject: [PATCH 17/77] Handle SDL event 0x304 by doing nothing (#3670) --- components/sdlutil/sdlinputwrapper.cpp | 5 +++++ components/sdlutil/sdlinputwrapper.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 6482f378e2..e8a0823f80 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -108,6 +108,11 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v case SDL_TEXTINPUT: mKeyboardListener->textInput(evt.text); break; + +#if SDL_VERSION_ATLEAST(2, 0, 4) + case SDL_KEYMAPCHANGED: + break; +#endif case SDL_JOYHATMOTION: //As we manage everything with GameController, don't even bother with these. case SDL_JOYAXISMOTION: case SDL_JOYBUTTONDOWN: diff --git a/components/sdlutil/sdlinputwrapper.hpp b/components/sdlutil/sdlinputwrapper.hpp index a821b9012c..62d6a565cc 100644 --- a/components/sdlutil/sdlinputwrapper.hpp +++ b/components/sdlutil/sdlinputwrapper.hpp @@ -6,6 +6,7 @@ #include #include +#include #include "OISCompat.hpp" #include "events.hpp" From 6140768783a7146eacf9f51f59af10b3bc716b89 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Dec 2016 23:21:33 +0100 Subject: [PATCH 18/77] 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 f94ee14b6c..544fb01996 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 b5e8c98b40c95cda7e2489e5cde2bf143b765544 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Dec 2016 23:21:33 +0100 Subject: [PATCH 19/77] 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 fece18d20c..14bce06e3b 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 20/77] 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 a095d5dd42..36a0f4085a 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 d9dd1a89eb..53eaf0996d 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 390a8e8235..f3c2aef77e 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 764b7f6459..24dcc6b3f4 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 cd135702303b540bfe9617c83d5c4806ca8eca57 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sun, 18 Dec 2016 04:37:59 +0100 Subject: [PATCH 21/77] ESSImporter: Import mark location --- apps/essimporter/convertplayer.cpp | 28 ++++++++++++++++++++++++++ apps/essimporter/importercontext.hpp | 2 +- apps/essimporter/importplayer.cpp | 13 +++++++++--- apps/essimporter/importplayer.hpp | 30 +++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index 9d82af022d..c363185ee0 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -47,6 +47,34 @@ namespace ESSImport controls.mVanityModeDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_VanityModeDisabled; controls.mWeaponDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawingDisabled; controls.mSpellDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawingDisabled; + + if (pcdt.mHasMark) + { + out.mHasMark = 1; + + const PCDT::PNAM::MarkLocation& mark = pcdt.mPNAM.mMarkLocation; + + ESM::CellId cell; + cell.mWorldspace = ESM::CellId::sDefaultWorldspace; + cell.mPaged = true; + + cell.mIndex.mX = mark.mCellX; + cell.mIndex.mY = mark.mCellY; + + // TODO: Figure out a better way to detect interiors. (0, 0) is a valid exterior cell. + if (mark.mCellX == 0 && mark.mCellY == 0) + { + cell.mWorldspace = pcdt.mMNAM; + cell.mPaged = false; + } + + out.mMarkedCell = cell; + out.mMarkedPosition.pos[0] = mark.mX; + out.mMarkedPosition.pos[1] = mark.mY; + out.mMarkedPosition.pos[2] = mark.mZ; + out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f; + out.mMarkedPosition.rot[2] = mark.mRotZ; + } } } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index fde247ebff..2288b149c2 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -63,7 +63,7 @@ namespace ESSImport playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; mPlayer.mCellId = playerCellId; //mPlayer.mLastKnownExteriorPosition - mPlayer.mHasMark = 0; // TODO + mPlayer.mHasMark = 0; mPlayer.mCurrentCrimeId = 0; // TODO mPlayer.mObject.blank(); mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index 9845ab072a..85a3c3fd5d 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -23,9 +23,12 @@ namespace ESSImport mKnownDialogueTopics.push_back(esm.getHString()); } + mHasMark = false; if (esm.isNextSub("MNAM")) - esm.skipHSub(); // If this field is here it seems to specify the interior cell the player is in, - // but it's not always here, so it's kinda useless + { + mHasMark = true; + mMNAM = esm.getHString(); + } esm.getHNT(mPNAM, "PNAM"); @@ -50,8 +53,12 @@ namespace ESSImport if (esm.isNextSub("NAM3")) esm.skipHSub(); + mHasENAM = false; if (esm.isNextSub("ENAM")) - esm.skipHSub(); + { + mHasENAM = true; + esm.getHT(mENAM); + } if (esm.isNextSub("LNAM")) esm.skipHSub(); diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 9f6b055c0e..7759944447 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -42,8 +42,11 @@ struct PCDT { PlayerFlags_ViewSwitchDisabled = 0x1, PlayerFlags_ControlsDisabled = 0x4, + PlayerFlags_Sleeping = 0x10, + PlayerFlags_Waiting = 0x40, PlayerFlags_WeaponDrawn = 0x80, PlayerFlags_SpellDrawn = 0x100, + PlayerFlags_InJail = 0x200, PlayerFlags_JumpingDisabled = 0x1000, PlayerFlags_LookingDisabled = 0x2000, PlayerFlags_VanityModeDisabled = 0x4000, @@ -68,19 +71,44 @@ struct PCDT struct PNAM { + struct MarkLocation + { + float mX, mY, mZ; // worldspace position + float mRotZ; // Z angle in radians + int mCellX, mCellY; // grid coordinates; for interior cells this is always (0, 0) + }; + int mPlayerFlags; // controls, camera and draw state unsigned int mLevelProgress; float mSkillProgress[27]; // skill progress, non-uniform scaled unsigned char mSkillIncreases[8]; // number of skill increases for each attribute - unsigned char mUnknown3[84]; + int mTelekinesisRangeBonus; // in units; seems redundant + float mVisionBonus; // range: <0.0, 1.0>; affected by light spells and Get/Mod/SetPCVisionBonus + int mDetectKeyMagnitude; // seems redundant + int mDetectEnchantmentMagnitude; // seems redundant + int mDetectAnimalMagnitude; // seems redundant + MarkLocation mMarkLocation; + unsigned char mUnknown3[40]; unsigned char mSpecIncreases[3]; // number of skill increases for each specialization unsigned char mUnknown4; }; + + struct ENAM + { + int mCellX; + int mCellY; + }; #pragma pack(pop) std::vector mFactions; PNAM mPNAM; + bool mHasMark; + std::string mMNAM; // mark cell name; can also be sDefaultCellname or region name + + bool mHasENAM; + ENAM mENAM; // last exterior cell + void load(ESM::ESMReader& esm); }; From 6d8d9e5dbc7c4af6db78d2db9129e7c5461fa323 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 18 Dec 2016 14:13:46 +0100 Subject: [PATCH 22/77] [macOS] Take Qt prefix path from Homebrew for CI resilience Recent CI failure was caused by path changed by Homebrew. --- CI/before_script.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 00a948c652..6d62255aaf 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -4,7 +4,7 @@ export CXX=clang++ export CC=clang DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" -QT_PATH="/usr/local/opt/qt55" +QT_PATH=`brew --prefix qt@5.5` mkdir build cd build From 9b74adbb2c036cc2863dae19ba7a8fee8d42302b Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 30 Oct 2016 19:30:54 +0100 Subject: [PATCH 23/77] [macOS] Reenable CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4f811ee0f4..574fe1ba8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ os: - linux -# - osx + - osx osx_image: xcode7.2 language: cpp sudo: required From 525af97ffc838605d36a3ebbdb7ceba2da40fa76 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 18 Dec 2016 14:23:04 +0100 Subject: [PATCH 24/77] [macOS] Use newer Xcode version on CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 574fe1ba8c..44db7b179d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ os: - linux - osx -osx_image: xcode7.2 +osx_image: xcode7.3 language: cpp sudo: required dist: trusty From 37c8a36dd888cdab8a149ede0653a355efe27cff Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 18 Dec 2016 14:40:48 +0100 Subject: [PATCH 25/77] [macOS] Parametrize Qt5 version used on CI --- .travis.yml | 1 + CI/before_install.osx.sh | 2 +- CI/before_script.osx.sh | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44db7b179d..ad311ef0ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ env: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE=" + - macos_qt_formula=qt@5.5 addons: apt: sources: diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 8a306186e2..a2ea720e53 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -5,7 +5,7 @@ brew update brew rm cmake || true brew rm pkgconfig || true brew rm qt5 || true -brew install cmake pkgconfig qt55 +brew install cmake pkgconfig $macos_qt_formula curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 6d62255aaf..6955825a40 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -4,7 +4,7 @@ export CXX=clang++ export CC=clang DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" -QT_PATH=`brew --prefix qt@5.5` +QT_PATH=`brew --prefix $macos_qt_formula` mkdir build cd build From 2967fbdf4c1f53e11ecb5d7281e1697625320568 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 18 Dec 2016 14:43:50 +0100 Subject: [PATCH 26/77] [CI] Fix email notification configuration --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad311ef0ef..3e98b1f961 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,9 +63,9 @@ script: - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi notifications: - recipients: - - corrmage+travis-ci@gmail.com email: + recipients: + - corrmage+travis-ci@gmail.com on_success: change on_failure: always irc: From 9c56ecac770dd22e107917840f59777b437d7b48 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 19 Dec 2016 15:28:27 +0100 Subject: [PATCH 27/77] fixed changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b4bd34e5..87d8267532 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 28/77] 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 0f414972fb..bd0d2ea4ad 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 8c55c37329..80a6228035 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 23a6f497ff..af8a47ccf3 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 163995f6f9..20aef4c17c 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 97fa98b3b4..e552f4683c 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 04c67fcb63..ed06f58c5a 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 c9315283dc..ab1c0afc66 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 29/77] 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 80a6228035..43a4efc7a0 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 dc1f788cff5c44d9c24c5ccc767fa4fa27ac5fc5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Dec 2016 16:49:37 +0100 Subject: [PATCH 30/77] Use osg::PI over M_PI --- apps/openmw/CMakeLists.txt | 1 - apps/openmw/mwrender/sky.cpp | 6 +++--- apps/openmw/mwworld/weather.cpp | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index ce859bc3e0..baa523654b 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -215,7 +215,6 @@ if (MSVC) if (CMAKE_CL_64) set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj") endif (CMAKE_CL_64) - add_definitions("-D_USE_MATH_DEFINES") endif (MSVC) if (WIN32) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index fba5f17b2b..57dd29c330 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -943,8 +943,8 @@ public: void setState(const MoonState& state) { - float radsX = ((state.mRotationFromHorizon) * M_PI) / 180.0f; - float radsZ = ((state.mRotationFromNorth) * M_PI) / 180.0f; + float radsX = ((state.mRotationFromHorizon) * static_cast(osg::PI)) / 180.0f; + float radsZ = ((state.mRotationFromNorth) * static_cast(osg::PI)) / 180.0f; osg::Quat rotX(radsX, osg::Vec3f(1.0f, 0.0f, 0.0f)); osg::Quat rotZ(radsZ, osg::Vec3f(0.0f, 0.0f, 1.0f)); @@ -954,7 +954,7 @@ public: // The moon quad is initially oriented facing down, so we need to offset its X-axis // rotation to rotate it to face the camera when sitting at the horizon. - osg::Quat attX((-M_PI / 2.0f) + radsX, osg::Vec3f(1.0f, 0.0f, 0.0f)); + osg::Quat attX((-static_cast(osg::PI) / 2.0f) + radsX, osg::Vec3f(1.0f, 0.0f, 0.0f)); mTransform->setAttitude(attX * rotZ); setPhase(state.mPhase); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 48ab1187f5..b7c5736bdf 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -695,9 +695,9 @@ void WeatherManager::update(float duration, bool paused) double theta; if ( !is_night ) { - theta = M_PI * (adjustedHour - mSunriseTime) / dayDuration; + theta = static_cast(osg::PI) * (adjustedHour - mSunriseTime) / dayDuration; } else { - theta = M_PI * (1.f - (adjustedHour - adjustedNightStart) / nightDuration); + theta = static_cast(osg::PI) * (1.f - (adjustedHour - adjustedNightStart) / nightDuration); } osg::Vec3f final( From 561e0cbbf986f889829d9c4d40b30ad6c2d8aca6 Mon Sep 17 00:00:00 2001 From: logzero Date: Tue, 6 Dec 2016 22:20:31 +0100 Subject: [PATCH 31/77] 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 544fb01996..e6ef5328c6 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 32/77] 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 e6ef5328c6..bf05897971 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 33/77] 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 bf05897971..3699eeebac 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 34/77] 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 3699eeebac..0d0a6e9f9b 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 35/77] 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 0d0a6e9f9b..494ff681cd 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 15cdc06162ce521481019dbfa0fa93be86ba7e5d Mon Sep 17 00:00:00 2001 From: Ryan Tucker Date: Wed, 21 Dec 2016 22:04:52 -0800 Subject: [PATCH 36/77] Finished transferring settings guide from wiki --- docs/source/openmw-mods/settings/game.rst | 2 +- docs/source/openmw-mods/settings/general.rst | 2 +- docs/source/openmw-mods/settings/input.rst | 2 +- docs/source/openmw-mods/settings/saves.rst | 2 +- docs/source/openmw-mods/settings/sound.rst | 103 ++++++++++++- docs/source/openmw-mods/settings/video.rst | 134 ++++++++++++++++- docs/source/openmw-mods/settings/water.rst | 44 +++++- docs/source/openmw-mods/settings/windows.rst | 150 ++++++++++++++++++- 8 files changed, 416 insertions(+), 23 deletions(-) diff --git a/docs/source/openmw-mods/settings/game.rst b/docs/source/openmw-mods/settings/game.rst index 1feb3f7e98..7f83e45f30 100644 --- a/docs/source/openmw-mods/settings/game.rst +++ b/docs/source/openmw-mods/settings/game.rst @@ -1,5 +1,5 @@ Game Settings -############ +############# show owned ---------- diff --git a/docs/source/openmw-mods/settings/general.rst b/docs/source/openmw-mods/settings/general.rst index 8813f5666b..e71e829779 100644 --- a/docs/source/openmw-mods/settings/general.rst +++ b/docs/source/openmw-mods/settings/general.rst @@ -1,5 +1,5 @@ General Settings -############ +################ anisotropy ---------- diff --git a/docs/source/openmw-mods/settings/input.rst b/docs/source/openmw-mods/settings/input.rst index d7903c1193..8c7b2a42ba 100644 --- a/docs/source/openmw-mods/settings/input.rst +++ b/docs/source/openmw-mods/settings/input.rst @@ -1,5 +1,5 @@ Input Settings -############ +############## grab cursor ----------- diff --git a/docs/source/openmw-mods/settings/saves.rst b/docs/source/openmw-mods/settings/saves.rst index 47fcea31be..39354a4c72 100644 --- a/docs/source/openmw-mods/settings/saves.rst +++ b/docs/source/openmw-mods/settings/saves.rst @@ -1,5 +1,5 @@ Saves Settings -############ +############## character --------- diff --git a/docs/source/openmw-mods/settings/sound.rst b/docs/source/openmw-mods/settings/sound.rst index 8d11307f39..49c4bb6e90 100644 --- a/docs/source/openmw-mods/settings/sound.rst +++ b/docs/source/openmw-mods/settings/sound.rst @@ -1,9 +1,102 @@ Sound Settings -############ +############## -scaling factor --------------- +device +------ + +:Type: string +:Range: +:Default: "" + +This string setting determines which audio device to use. A blank or missing setting means to use the default device, which should usually be sufficient, but if you need to explicitly specify a device use this setting. + +The names of detected devices can be found in the openmw.log file in your configuration directory. + +The default value is the empty string. This setting can only be configured by editing the settings configuration file. + +master volume +------------- :Type: floating point -:Range: > 0.0 -:Default: 1.0 \ No newline at end of file +:Range: 0.0 to 1.0 +:Default: 1.0 + +This floating point setting controls the overall volume. The master volume is multiplied with all other volume settings to determine the final volume. + +The default value is 1.0. Valid values range from 0.0 (silent) to 1.0 (maximum volume). This setting can be changed in game using the Master slider from the Audio panel of the Options menu. + +footsteps volume +---------------- + +:Type: floating point +:Range: 0.0 to 1.0 +:Default: 0.2 + +This floating point setting controls the volume of footsteps from the character and other actors. + +The default value is 0.2. Valid values range from 0.0 (silent) to 1.0 (maximum volume). This setting can be changed in game using the Footsteps slider from the Audio panel of the Options menu. + +music volume +------------ + +:Type: floating point +:Range: 0.0 to 1.0 +:Default: 0.5 + +This floating point setting controls the volume for music tracks. + +The default value is 0.5. Valid values range from 0.0 (silent) to 1.0 (maximum volume). This setting can be changed in game using the Music slider from the Audio panel of the Options menu. + +sfx volume +---------- + +:Type: floating point +:Range: 0.0 to 1.0 +:Default: 1.0 + +This floating point setting controls the volume for special sound effects such as combat noises. + +The default value is 1.0. Valid values range from 0.0 (silent) to 1.0 (maximum volume). This setting can be changed in game using the Effects slider from the Audio panel of the Options menu. + +voice volume +------------ + +:Type: floating point +:Range: 0.0 to 1.0 +:Default: 0.8 + +This floating point setting controls the volume for spoken dialog from NPCs. + +The default value is 0.8. Valid values range from 0.0 (silent) to 1.0 (maximum volume). This setting can be changed in game using the Voice slider from the Audio panel of the Options menu. + +buffer cache min +---------------- + +:Type: integer +:Range: > 0 +:Default: 14 + +This integer setting determines the minimum size of the sound buffer cache in megabytes. When the cache reaches the size specified by the buffer cache max setting, old buffers will be unloaded until it's using no more memory than specified by this setting. This setting must be less than or equal to the buffer cache max setting. + +The default value is 14. This setting can only be configured by editing the settings configuration file. This setting was added in OpenMW version 0.38. + +hrtf enable +----------- + +:Type: integer +:Range: -1, 0, 1 +:Default: -1 + +This integer setting determines whether to enable head-related transfer function (HRTF) audio processing. HRTF audio processing creates the perception of sounds occurring in a three dimensional space when wearing headphones. Enabling HRTF may also require an OpenAL Soft version greater than 1.17.0, and possibly some operating system configuration. A value of 0 disables HRTF processing, while a value of 1 explicitly enables HRTF processing. +The default value is -1, which should enable the feature automatically for most users when possible. This setting can only be configured by editing the settings configuration file. This setting was added in OpenMW version 0.38. + +hrtf +---- + +:Type: string +:Range: +:Default: "" + +This string setting specifies which HRTF profile to use when HRTF is enabled. Blank means use the default. This setting has no effect if HRTF is not enabled based on the hrtf enable setting. Allowed values for this field are enumerated in openmw.log file is an HRTF enabled ausio system is installed. + +The default value is the empty string, which uses the default profile. This setting can only be configured by editing the settings configuration file. This setting was added in OpenMW version 0.38. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/video.rst b/docs/source/openmw-mods/settings/video.rst index ba345a6e99..a31af51031 100644 --- a/docs/source/openmw-mods/settings/video.rst +++ b/docs/source/openmw-mods/settings/video.rst @@ -1,9 +1,135 @@ Video Settings -############ +############## -scaling factor --------------- +resolution x +------------ + +:Type: integer +:Range: > 0 +:Default: 800 + +This setting determines the horizontal resolution of the OpenMW game window. Larger values produce more detailed images within the constraints of your graphics hardware but also significantly reduce the frame rate. + +The default value is 800. The window resolution can be selected from a menu of common screen sizes in the Video tab of the Video Panel of the Options menu, or in the Graphics tab of the OpenMW Launcher. The horizontal resolution can also be set to a custom value in the Graphics tab of the OpenMW Launcher. + +resolution y +------------ + +:Type: integer +:Range: > 0 +:Default: 600 + +This setting determines the vertical resolution of the OpenMW game window. Larger values produce more detailed images within the constraints of your graphics hardware but also significantly reduce the frame rate. + +The default value is 600. The window resolution can be selected from a menu of common screen sizes in the Video tab of the Video Panel of the Options menu, or in the Graphics tab of the OpenMW Launcher. The vertical resolution can also be set to a custom value in the Graphics tab of the OpenMW Launcher. + +fullscreen +---------- + +:Type: boolean +:Range: True/False +:Default: False + +This boolean setting determines whether the entire screen is used for the specified resolution. + +The default value is false. This setting can be toggled in game using the Fullscreen button in the Video tab of the Video panel in the Options menu. It can also be toggled with the Full Screen check box in the Graphics tab of the OpenMW Launcher. + +screen +------ + +:Type: integer +:Range: >= 0 +:Default: 0 + +This integer setting determines which screen the game will open on in multi-monitor configurations. This setting is particularly important when the fullscreen setting is true, since this is the only way to control which screen is used, but it can also be used to control which screen a normal window or a borderless window opens on as well. The screens are numbered in increasing order, beginning with 0. + +The default value is 0. This setting can be selected from a pull down menu in the Graphics tab of the OpenMW Launcher, but cannot be changed during game play. + +minimize on focus loss +---------------------- + +:Type: boolean +:Range: True/False +:Default: False + +Minimize the OpenMW window if it loses cursor focus. This setting is primarily useful for single screen configurations, so that the OpenMW screen in full screen mode can be minimized when the operating system regains control of the mouse and keyboard. On multiple screen configurations, disabling this option makes it easier to switch between screens while playing OpenMW. + +Note that a minimized game window consumes less system resources and produces less heat, since the game does not need to render in minimized state. It is therefore advisable to minimize the game during pauses (either via use of this setting, or by minimizing the window manually). + +This setting has no effect if the fullscreen setting is false. + +Developer note: corresponds to SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS. + +The default value is true. This setting can only be configured by editing the settings configuration file. + +window border +------------- + +:Type: boolean +:Range: True/False +:Default: True + +This boolean setting determines whether there's an operating system border drawn around the OpenMW window. If this setting is true, the window can be moved and resized with the operating system window controls. If this setting is false, the window has no operating system border. + +This setting has no effect if the fullscreen setting is true. + +The default value is true. This setting can be toggled in game using the Window Border button in the Video tab of the Video panel in the Options menu. It can also be toggled with the Window Border check box in the OpenMW Launcher. + +antialiasing +------------ + +:Type: integer +:Range: 0, 2, 4, 8, 16 +:Default: 0 + +This integer setting controls anti-aliasing. Anti-aliasing is a technique designed to improve the appearance of polygon edges, so they do not appear to be "jagged". Anti-aliasing can smooth these edges at the cost of a minor reduction in the frame rate. A value of 0 disables anti-aliasing. Other powers of two (e.g. 2, 4, 8, 16) are supported according to the capabilities of your graphics hardware. Higher values do a better job of smoothing out the image but have a greater impact on frame rate. + +This setting can be configured from a list of valid choices in the Graphics panel of the OpenMW Launcher, but cannot be changed during game play - due to a technical limitation that may be addressed in a future version of OpenMW. + +vsync +----- + +:Type: boolean +:Range: True/False +:Default: False + +This boolean setting determines whether frame draws are synchronized with the vertical refresh rate of your monitor. Enabling this setting can reduce screen tearing, a visual defect caused by updating the image buffer in the middle of a screen draw. Enabling this option typically implies limiting the framerate to 60 frames per second, but may also introduce additional delays caused by having to wait until the appropriate time (the vertical blanking interval) to draw a frame. + +The default value is false. This setting can be adjusted in game using the VSync button in the Video tab of the Video panel in the Options menu. It can also be changed by toggling the Vertical Sync check box in the Graphics tab of the OpenMW Launcher. + +framerate limit +--------------- + +:Type: floating point +:Range: >= 0.0 +:Default: 0.0 + +This floating point setting determines the maximum frame rate in frames per second. If this setting is 0.0, the frame rate is unlimited. + +There are several reasons to consider capping your frame rate, especially if you're already experiencing a relatively high frame rate (greater than 60 frames per second). Lower frame rates will consume less power and generate less heat and noise. Frame rates above 60 frames per second rarely produce perceptible improvements in visual quality, but may improve input responsiveness. Capping the frame rate may in some situations reduce the perception of choppiness (highly variable frame rates during game play) by lowering the peak frame rates. + +This setting interacts with the vsync setting in the Video section in the sense that enabling vertical sync limits the frame rate to the refresh rate of your monitor (often 60 frames per second). Choosing to limit the frame rate using this setting instead of vsync may reduce input lag due to the game not having to wait for the vertical blanking interval. + +The default value is 0.0. This setting can only be configured by editing the settings configuration file. This setting was added in OpenMW 0.37. + +contrast +-------- :Type: floating point :Range: > 0.0 -:Default: 1.0 \ No newline at end of file +:Default: 1.0 + +This floating point setting controls the contrast correction for all video in the game. + +The default value is 1.0. This setting can only be configured by editing the settings configuration file. This setting does not currently work under Linux. + +gamma +----- + +:Type: floating point +:Range: > 0.0 +:Default: 1.0 + +This floating point setting controls the gamma correction for all video in the game. Gamma is an exponent that makes colors brighter if greater than 1.0 and darker if less than 1.0. + +The default value is 1.0. This setting can be changed in the Detail tab of the Video panel of the Options menu. This setting does not currently work under Linux, and the in-game setting in the Options menu has been disabled. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/water.rst b/docs/source/openmw-mods/settings/water.rst index 0d115f9436..b2b9a00c21 100644 --- a/docs/source/openmw-mods/settings/water.rst +++ b/docs/source/openmw-mods/settings/water.rst @@ -1,9 +1,43 @@ Water Settings ############ -scaling factor --------------- +.. note:: + The settings for the water shader are difficult to describe, but can be seen immediately in the Water tab of the Video panel in the Options menu. Changes there will be saved to these settings. It is suggested to stand on the shore of a moderately broad body of water with trees or other objects on the far shore to test reflection textures, underwater plants in shallow water near by to test refraction textures, and some deep water visible from your location to test deep water visibility. -:Type: floating point -:Range: > 0.0 -:Default: 1.0 \ No newline at end of file +shader +------ + +:Type: boolean +:Range: True/False +:Default: False + +This boolean setting enables or disables the water shader, which results in much more realistic looking water surfaces, including reflected objects and a more detailed wavy surface. + +The default value is false. This setting can be toggled with the Shader button in the Water tab of the Video panel of the Options menu. + +rtt size +-------- + +:Type: integer +:Range: > 0 +:Default: 512 + +The integer setting determines the size of the texture used for reflection and refraction (if enabled). For reflection, the texture size determines the detail of reflected images on the surface of the water. For refraction, the texture size determines the detail of the objects on the other side of the plane of water (which have a wavy appearance caused by the refraction). RTT is an acronym for Render to Texture which allows rendering of the scene to be saved as a texture. +Higher values produces better visuals and result in a marginally lower frame rate depending on your graphics hardware. + +In the Water tab of the Video panel of the Options menu, the choices are Low (512), Medium (1024) and High (2048). This setting has no effect if the shader setting is false. It is recommended to use values that are a power of two because this results in more efficient use of video hardware. + +This setting has no effect if the shader setting is false. + +refraction +---------- + +:Type: boolean +:Range: True/False +:Default: False + +This boolean setting enables the refraction rendering feature of the water shader. Refraction causes deep water to be more opaque and objects seen through the plane of the water to have a wavy appearance. Enabling this feature results in better visuals, and a marginally lower frame rate depending on your graphics hardware. + +This setting has no effect if the shader setting is false. + +The default setting is false. This setting can be toggled with the Refraction button in the Water tab of the Video panel of the Options menu. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/windows.rst b/docs/source/openmw-mods/settings/windows.rst index 1580ac2204..9472b9a81e 100644 --- a/docs/source/openmw-mods/settings/windows.rst +++ b/docs/source/openmw-mods/settings/windows.rst @@ -1,9 +1,149 @@ Windows Settings ############ -scaling factor --------------- +:Type: floating point +:Range: 0.0 to 1.0 -:Type: floating point -:Range: > 0.0 -:Default: 1.0 \ No newline at end of file +This section controls the location and size of each window in GUI mode. Each setting is a floating point number representing a *fraction* of the resolution x or resolution y setting in the Video Settings Section. The X and Y values locate the top left corner of the window, while the W value determines the width of the window and the H value determines the height of the window. + +Unlike the documentation for most sections which lists the exact setting name, this page instead lists the names of the windows. For example, to configure the alchemy window, the actual settings would be:: + + alchemy x = 0.25 + alchemy y = 0.25 + alchemy h = 0.5 + alchemy w = 0.5 + +Each window in the GUI mode remembers it's previous location when exiting the game. By far the easiest way to configure these settings is to simply move the windows around in game. Hand editing the configuration file might result in some fine tuning for alignment, but the settings will be overwritten if a window is moved. + +.. note:: + To scale the windows, making the widgets proportionally larger, see the scaling factor setting instead. + +stats +----- + +:Default: x = 0.0 + y = 0.0 + h = 0.375 + w = 0.4275 + +The stats window, displaying level, race, class, skills and stats. Activated by clicking on any of the three bars in the lower left corner of the HUD. + +spells +------ + +:Default: x = 0.625 + y = 0.5725 + h = 0.375 + w = 0.4275 + +The spells window, displaying powers, spells, and magical items. Activated by clicking on the spells widget (third from left) in the bottom left corner of the HUD. + +map +--- + +:Default: x = 0.625 + y = 0.0 + h = 0.375 + w = 0.5725 + +The local and world map window. Activated by clicking on the map widget in the bottom right corner of the HUD. + +dialogue +-------- + +:Default: x = 0.095 + y = 0.095 + h = 0.810 + w = 0.810 + +The dialog window, for talking with NPCs. Activated by clicking on a NPC. + +alchemy +------- + +:Default: x = 0.25 + y = 0.25 + h = 0.5 + w = 0.5 + +The alchemy window, for crafting potions. Activated by dragging an alchemy tool on to the rag doll. Unlike most other windows, this window hides all other windows when opened. + +console +------- + +:Default: x = 0.0 + y = 0.0 + h = 1.0 + w = 0.5 + +The console command window. Activated by pressing the tilde (~) key. + +inventory +--------- + +:Default: x = 0.0 + y = 0.4275 + h = 0.6225 + w = 0.5725 + +The inventory window, displaying the paper doll and possessions, when activated by clicking on the inventory widget (second from left) in the bottom left corner of the HUD. + +inventory container +------------------- + +:Default: x = 0.0 + y = 0.4275 + h = 0.6225 + w = 0.5725 + +The player's inventory window while searching a container, showing the contents of the character's inventory. Activated by clicking on a container. The same window is used for searching dead bodies, and pickpocketing people. + +inventory barter +---------------- + +:Default: x = 0.0 + y = 0.4275 + h = 0.6225 + w = 0.5725 + +The player's inventory window while bartering. It displays goods owned by the character while bartering. Activated by clicking on the Barter choice in the dialog window for an NPC. + +inventory companion +------------------- + +:Default: x = 0.0 + y = 0.4275 + h = 0.6225 + w = 0.5725 + +The player's inventory window while interacting with a companion. The companion windows were added in the Tribunal expansion, but are available everywhere in the OpenMW engine. + +container +--------- + +:Default: x = 0.25 + y = 0.0 + h = 0.75 + w = 0.375 + +The container window, showing the contents of the container. Activated by clicking on a container. The same window is used for searching dead bodies, and pickpocketing people. + +barter +------ + +:Default: x = 0.25 + y = 0.0 + h = 0.75 + w = 0.375 + +The NPC bartering window, displaying goods owned by the shopkeeper while bartering. Activated by clicking on the Barter choice in the dialog window for an NPC. + +companion +--------- + +:Default: x = 0.25 + y = 0.0 + h = 0.75 + w = 0.375 + +The NPC's inventory window while interacting with a companion. The companion windows were added in the Tribunal expansion, but are available everywhere in the OpenMW engine. \ No newline at end of file From cf496287f76026c97400c5f49584b6fccff37c87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 22 Dec 2016 14:39:03 +0100 Subject: [PATCH 37/77] 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 b7e24a7ee8..72e1c09f3f 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 544fb01996..7cfa38ff2a 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 9f8bae280d..d1b71bbd53 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 38/77] 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 5e65bad7cb..ff3427e12d 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 39/77] 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 37a84b2879..d51918152b 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 40/77] 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 43a4efc7a0..3063b3268f 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 + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); + + // Add price for the travelling followers 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; - - if ((follower.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= 800*800) - ++travellingFollowers; - } + 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 ab1c0afc66..5162cac660 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 6191ee9f6f..ab28d2c962 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 41/77] 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 494ff681cd..ab7117a106 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 42/77] 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 ab7117a106..14e05f101e 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 43/77] [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 3e98b1f961..a669700c01 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 6955825a40..f3d0f716b1 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 44/77] 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 c363185ee0..4a4a9a573e 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 2288b149c2..6921cce925 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 85a3c3fd5d..8c275a2868 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 7759944447..924522383b 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 45/77] 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 14e05f101e..2a3cb6137f 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 420ca1a9e5..feda68ca53 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 7b7d0391e6..0297c9e076 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 46/77] 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 2a3cb6137f..db72ac6365 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 47/77] 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 23a6f497ff..8c1a88e3fc 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 48/77] 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 8c1a88e3fc..2f1c55a2a9 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 49/77] 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 d8dae8b79a..fbd3819e24 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 a2e995cb38..fbe864ca07 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 50/77] 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 2f1c55a2a9..d2d07cf28c 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 51/77] 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 d2d07cf28c..441e7d0f47 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 52/77] 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 441e7d0f47..497ec73849 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 53/77] 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 ab28d2c962..c582187502 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 54/77] 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 f94cf9b43b..fb259ff5ff 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 55/77] 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 db72ac6365..87a7ab3e56 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 56/77] Update AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 185b5ee665..93b85cb594 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 57/77] Update AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 93b85cb594..190a2f3453 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 58/77] 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 4d342336d0..150d70c93d 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 59/77] 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 6663b8b294..eae63c514b 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 df8aa9962c..1c44439fd0 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 60/77] 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 eae63c514b..5cf1ecd360 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 61/77] 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 43d77b99d6..1d1d8cf25f 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 62/77] 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 1d1d8cf25f..489ab83ec1 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 63/77] [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 a2ea720e53..941c10a029 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 64/77] [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 941c10a029..49af86a3e2 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 65/77] 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 3102354a64..830c2bcacc 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)); } From 577e9838b36498a38fcfb9453079e2f69c97e2c4 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 8 Jan 2017 17:35:07 +0000 Subject: [PATCH 66/77] Correct lack of quotes in instructions for adding data= lines --- docs/source/openmw-mods/differences.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/openmw-mods/differences.rst b/docs/source/openmw-mods/differences.rst index 61f9b2a75a..af90bc8d67 100644 --- a/docs/source/openmw-mods/differences.rst +++ b/docs/source/openmw-mods/differences.rst @@ -21,7 +21,7 @@ To install mods via this new feature: #. Open ``openmw.cfg`` with your preffered text editor. It is located as described in https://wiki.openmw.org/index.php?title=Paths and *not* in your OpenMW root directory. #. Find or search for ``data=``. This is located very near the bottom of the file. -#. Add a new line below this line and make a new entry of the format ``data=path/to/your/mod`` +#. Add a new line below this line and make a new entry of the format ``data="path/to/your/mod"`` #. Make as many of these entries as you need for each mod folder you want to include. #. Save ``openmw.cfg`` From f1f920981484a8fe8a75eb5db23fb06b2a2a4f68 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sun, 8 Jan 2017 20:52:04 +0100 Subject: [PATCH 67/77] Creatures now auto-equip shields (Fixes #3704) --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 52 +++++++++++++++++++++++--- apps/openmw/mwworld/inventorystore.hpp | 3 ++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index ff4ae61486..9e5afb66aa 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -164,7 +164,7 @@ namespace MWClass getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); if (hasInventory) - getInventoryStore(ptr).autoEquip(ptr); + getInventoryStore(ptr).autoEquipShield(ptr); } } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index d1b71bbd53..7948327332 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -139,7 +139,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves if (actorPtr != MWMechanics::getPlayer() - && !(actorPtr.getClass().isNpc() && actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())) + && actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf()) { std::string type = itemPtr.getTypeName(); if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name()) @@ -237,10 +237,6 @@ bool MWWorld::InventoryStore::canActorAutoEquip(const MWWorld::Ptr& actor, const void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { - if (!actor.getClass().isNpc()) - // autoEquip is no-op for creatures - return; - const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor); @@ -443,6 +439,50 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) } } +void MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor) +{ + bool updated = false; + + mUpdatesEnabled = false; + for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter) + { + if (iter->get()->mBase->mData.mType != ESM::Armor::Shield) + continue; + + if (iter->getClass().canBeEquipped(*iter, actor).first != 1) + continue; + + if (iter->getClass().getItemHealth(*iter) <= 0) + continue; + + std::pair, bool> shieldSlots = + iter->getClass().getEquipmentSlots(*iter); + + if (shieldSlots.first.empty()) + continue; + + int slot = shieldSlots.first[0]; + const ContainerStoreIterator& shield = mSlots[slot]; + + if (shield != end() + && shield.getType() == Type_Armor && shield->get()->mBase->mData.mType == ESM::Armor::Shield) + { + if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter)) + continue; + } + + equip(slot, iter, actor); + updated = true; + } + mUpdatesEnabled = true; + + if (updated) + { + fireEquipmentChangedEvent(actor); + updateMagicEffects(actor); + } +} + const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() const { return mMagicEffects; @@ -622,7 +662,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor // If an armor/clothing item is removed, try to find a replacement, // but not for the player nor werewolves. if (wasEquipped && (actor != MWMechanics::getPlayer()) - && !(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())) + && actor.getClass().isNpc() && !actor.getClass().getNpcStats(actor).isWerewolf()) { std::string type = item.getTypeName(); if (type == typeid(ESM::Armor).name() || type == typeid(ESM::Clothing).name()) diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 7e6a259c45..f625e4cd1b 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -162,6 +162,9 @@ namespace MWWorld void autoEquip (const MWWorld::Ptr& actor); ///< Auto equip items according to stats and item value. + void autoEquipShield(const MWWorld::Ptr& actor); + ///< Auto-equip the shield with most health. + const MWMechanics::MagicEffects& getMagicEffects() const; ///< Return magic effects from worn items. From 3f272e293599b248aa77a9f34e21bee550c93454 Mon Sep 17 00:00:00 2001 From: Ryan Tucker Date: Sun, 8 Jan 2017 11:58:56 -0800 Subject: [PATCH 68/77] Added fonts and paths documents We should have paths on RTD as well because if we change the paths again, we want the versioning to reflect that without a million notes informing us of the change. Also fixed an autocount to work correctly. --- docs/source/openmw-mods/differences.rst | 2 +- docs/source/openmw-mods/font.rst | 77 ++++++++++++++++++++++ docs/source/openmw-mods/index.rst | 1 + docs/source/openmw-mods/mod-install.rst | 2 +- docs/source/openmw-mods/paths.rst | 28 ++++++++ docs/source/openmw-mods/settings/game.rst | 21 ++++++ docs/source/openmw-mods/settings/index.rst | 9 +-- docs/source/openmw-mods/settings/sound.rst | 11 ++++ 8 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 docs/source/openmw-mods/font.rst create mode 100644 docs/source/openmw-mods/paths.rst diff --git a/docs/source/openmw-mods/differences.rst b/docs/source/openmw-mods/differences.rst index 61f9b2a75a..dbbae4dcad 100644 --- a/docs/source/openmw-mods/differences.rst +++ b/docs/source/openmw-mods/differences.rst @@ -19,7 +19,7 @@ The largest difference between OpenMW and Morrowind in terms of data structure i To install mods via this new feature: -#. Open ``openmw.cfg`` with your preffered text editor. It is located as described in https://wiki.openmw.org/index.php?title=Paths and *not* in your OpenMW root directory. +#. Open ``openmw.cfg`` with your preffered text editor. It is located as described in :doc:`paths` and *not* in your OpenMW root directory. #. Find or search for ``data=``. This is located very near the bottom of the file. #. Add a new line below this line and make a new entry of the format ``data=path/to/your/mod`` #. Make as many of these entries as you need for each mod folder you want to include. diff --git a/docs/source/openmw-mods/font.rst b/docs/source/openmw-mods/font.rst new file mode 100644 index 0000000000..5f01b12d93 --- /dev/null +++ b/docs/source/openmw-mods/font.rst @@ -0,0 +1,77 @@ +Fonts +##### + +Morrowind .fnt fonts +-------------------- + +Morrowind uses a custom ``.fnt`` file format. It is not compatible with the Windows Font File ``.fnt`` format, nor compatible with ``.fnt`` formats from any other Bethesda games. To our knowledge, the format is undocumented and no tools for viewing or editing these fonts exist. + +OpenMW can load this format and convert it on the fly into something usable (see font loader `source code `_). In OpenMW 0.32, an --export-fonts command line option was added to write the converted font (a PNG image and an XML file describing the position of each glyph in the image) to the current directory. + +TrueType fonts +-------------- + +Unlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts. This is the recommended way to create new fonts. + +- To replace the primary "Magic Cards" font: + + #. Download `Pelagiad `_ by Isak Larborn (aka Isaskar). + #. Install the ``openmw_font.xml`` file into ``resources/mygui/openmw_font.xml`` in your OpenMW installation. + #. Copy ``Pelagiad.ttf`` into ``resources/mygui/`` as well. + #. If desired, you can now delete the original ``Magic_Cards.*`` files from your Data Files/Fonts directory. +- You can also replace the Daedric font: + + #. Download `Ayembedt `_ by Georg Duffner. + #. Install ``OMWAyembedt.otf`` into ``/resources/mygui/`` folder in your OpenMW installation. + #. Add the following lines to openmw_font.xml:: + + + + + + + + + + + + + + + + #. This font is missing a few glyphs (mostly punctuation), but is complete in the primary glyphs. If desired, you can now delete the original ``daedric.*`` files from your Data Files/Fonts directory. + +- Another replacement for the Daedric font is `Oblivion `_ by Dongle. + + #. Install the ``Oblivion.ttf`` file resources/mygui/. + #. The openmw_fonts.xml entry is:: + + + + + + + + + + + + + + + + + + + + + + + + + + +Bitmap fonts +------------ + +Morrowind ``.fnt`` files are essentially a bitmap font, but using them is discouraged because of no Unicode support. MyGUI has its own format for bitmap fonts. An example can be seen by using the --export-fonts command line option (see above), which converts Morrowind ``.fnt`` to a MyGUI bitmap font. This is the recommended format to use if you wish to edit Morrowind's bitmap font or create a new bitmap font. \ No newline at end of file diff --git a/docs/source/openmw-mods/index.rst b/docs/source/openmw-mods/index.rst index 123f838a31..e948862e9b 100644 --- a/docs/source/openmw-mods/index.rst +++ b/docs/source/openmw-mods/index.rst @@ -16,3 +16,4 @@ The following document is the complete reference guide to modifying, or modding, mod-install settings/index convert_bump_mapped_mods + paths \ No newline at end of file diff --git a/docs/source/openmw-mods/mod-install.rst b/docs/source/openmw-mods/mod-install.rst index 85b0015bab..20ffbc9dc9 100644 --- a/docs/source/openmw-mods/mod-install.rst +++ b/docs/source/openmw-mods/mod-install.rst @@ -14,7 +14,7 @@ Install .. note:: There may be multiple levels of folders, but the location of the plugins must be the same as the resource folders. -3. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in https://wiki.openmw.org/index.php?title=Paths and *not* in your OpenMW root directory. +3. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in :doc:`paths` and *not* in your OpenMW root directory. #. Find or search for ``data=``. This is located very near the bottom of the file. If you are using Morrowind, this first entry should already point to your Morrowind data directory, ``Data Files``; otherwise it will point to your game file, ``.omwgame``. #. Create a new line underneath and type: ``data="path/to/your/data folder"`` Remember, the *data folder* is where your mod's plugin files are. The double quotes around this path name are *required*. #. Save your ``openmw.cfg`` file. diff --git a/docs/source/openmw-mods/paths.rst b/docs/source/openmw-mods/paths.rst new file mode 100644 index 0000000000..23e33190dc --- /dev/null +++ b/docs/source/openmw-mods/paths.rst @@ -0,0 +1,28 @@ +Paths +##### + +The following describes the locations for the various OpenMW file paths for different types of files on different operating systems. + +.. note:: + Actual location depends on your computer's setup. Username, harddrive, and language may vary. + +Configuration files and log files +--------------------------------- + +:Linux: ``$HOME/.config/openmw`` +:Mac: ``$HOME/Library/Preferences/openmw`` +:Windows: ``C:\Users\Username\Documents\my games\openmw`` + +Savegames +--------- + +:Linux: ``$HOME/.local/share/openmw/saves`` +:Mac: ``$HOME/Library/Application Support/openmw/saves`` +:Windows: ``C:\Users\Username\Documents\my games\openmw\saves`` + +Screenshots +----------- + +:Linux: ``$HOME/.local/share/openmw`` +:Mac: ``$HOME/Library/Application Support/openmw`` +:Windows: ``C:\Users\Username\Documents\my games\openmw`` diff --git a/docs/source/openmw-mods/settings/game.rst b/docs/source/openmw-mods/settings/game.rst index 7f83e45f30..40fd2e292c 100644 --- a/docs/source/openmw-mods/settings/game.rst +++ b/docs/source/openmw-mods/settings/game.rst @@ -23,3 +23,24 @@ If this boolean setting is true, the player character will always use the most p The default value is false. This setting can be toggled with the Always Use Best Attack button in the Prefs panel of the Options menu. +difficulty +---------- + +:Type: integer +:Range: -500 to 500 +:Default: 0 + +This integer setting adjusts the difficulty of the game and is intended to be in the range -100 to 100 inclusive. Given the default game setting for fDifficultyMult of 5.0, a value of -100 results in the player taking 80% of the usual damage, doing 6 times the normal damage. A value of 100 results in the player taking 6 times as much damage, but inflicting only 80% of the usual damage. Values less than -500 will result in the player receiving no damage, and values greater than 500 will result in the player inflicting no damage. + +The default value is 0. This setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu. + +show effect duration +-------------------- + +:Type: boolean +:Range: True/False +:Default: False + +Show the remaining duration of magic effects and lights if this boolean setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. + +The default value is false. This setting can only be configured by editing the settings configuration file. \ No newline at end of file diff --git a/docs/source/openmw-mods/settings/index.rst b/docs/source/openmw-mods/settings/index.rst index 86c7316da5..fa79c5efc3 100644 --- a/docs/source/openmw-mods/settings/index.rst +++ b/docs/source/openmw-mods/settings/index.rst @@ -2,14 +2,7 @@ Advanced Settings Configuration ############################### -This part of the guide will cover how to make modifications to the more arcane settings in OpenMW, most of which are not available from in-game menus, to optimize or customize your OpenMW experience. If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this will be quite similar. All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory. This directory is located differently depending on your operating system, shown below: - -Windows: - ``C:\Users\\Documents\my games\openmw`` (hard drive, username and language may vary) -Mac OSX: - ``$HOME/Library/Preferences/openmw`` -Linux: - ``$HOME/.config/openmw`` +This part of the guide will cover how to make modifications to the more arcane settings in OpenMW, most of which are not available from in-game menus, to optimize or customize your OpenMW experience. If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this will be quite similar. All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory. See :doc:`paths` for this location. Although this guide attempts to be comprehensive and up to date. You will always be able to find the full list of settings available and their default values in ``settings-default.cfg`` in your main OpenMW installation directory. The ranges I have included with each setting are the physically possible ranges, not recommendations. diff --git a/docs/source/openmw-mods/settings/sound.rst b/docs/source/openmw-mods/settings/sound.rst index 49c4bb6e90..6537ee9508 100644 --- a/docs/source/openmw-mods/settings/sound.rst +++ b/docs/source/openmw-mods/settings/sound.rst @@ -80,6 +80,17 @@ This integer setting determines the minimum size of the sound buffer cache in me The default value is 14. This setting can only be configured by editing the settings configuration file. This setting was added in OpenMW version 0.38. +buffer cache max +---------------- + +:Type: integer +:Range: > 0 +:Default: 16 + +This integer setting determines the maximum size of the sound buffer cache in megabytes. When the cache reaches this size, old buffers will be unloaded until it reaches the size specified by the buffer cache min setting. This setting must be greater than or equal to the buffer cache min setting. + +The default value is 16. This setting can only be configured by editing the settings configuration file. This setting was added in OpenMW version 0.38. + hrtf enable ----------- From b02bbe2bf492ddaaa84f25be24411d45f7a2b041 Mon Sep 17 00:00:00 2001 From: Ryan Tucker Date: Sun, 8 Jan 2017 12:03:53 -0800 Subject: [PATCH 69/77] Placed note under proper list item --- docs/source/openmw-mods/mod-install.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/openmw-mods/mod-install.rst b/docs/source/openmw-mods/mod-install.rst index 20ffbc9dc9..4d6b5f42e6 100644 --- a/docs/source/openmw-mods/mod-install.rst +++ b/docs/source/openmw-mods/mod-install.rst @@ -11,10 +11,11 @@ Install #. Locate the plugin files, ``.esp`` or ``.omwaddon``. The folder containing the plugin files we will call your *data folder* #. Check that all resource folders (``Meshes``, ``Textures``, etc.) containing additional resource files (the actual meshes, textures, etc.) are in the *data folder*. - -.. note:: - There may be multiple levels of folders, but the location of the plugins must be the same as the resource folders. -3. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in :doc:`paths` and *not* in your OpenMW root directory. + + .. note:: + There may be multiple levels of folders, but the location of the plugins must be the same as the resource folders. + +#. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in :doc:`paths` and *not* in your OpenMW root directory. #. Find or search for ``data=``. This is located very near the bottom of the file. If you are using Morrowind, this first entry should already point to your Morrowind data directory, ``Data Files``; otherwise it will point to your game file, ``.omwgame``. #. Create a new line underneath and type: ``data="path/to/your/data folder"`` Remember, the *data folder* is where your mod's plugin files are. The double quotes around this path name are *required*. #. Save your ``openmw.cfg`` file. From 072a678a60f3fa0cb05072f06e2d608dc7b65686 Mon Sep 17 00:00:00 2001 From: Ryan Tucker Date: Sun, 8 Jan 2017 19:11:07 -0800 Subject: [PATCH 70/77] Added fonts to index and a note on quotes --- docs/source/openmw-mods/index.rst | 1 + docs/source/openmw-mods/mod-install.rst | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/docs/source/openmw-mods/index.rst b/docs/source/openmw-mods/index.rst index e948862e9b..0219c037e1 100644 --- a/docs/source/openmw-mods/index.rst +++ b/docs/source/openmw-mods/index.rst @@ -15,5 +15,6 @@ The following document is the complete reference guide to modifying, or modding, differences mod-install settings/index + fonts convert_bump_mapped_mods paths \ No newline at end of file diff --git a/docs/source/openmw-mods/mod-install.rst b/docs/source/openmw-mods/mod-install.rst index 4d6b5f42e6..a726781733 100644 --- a/docs/source/openmw-mods/mod-install.rst +++ b/docs/source/openmw-mods/mod-install.rst @@ -18,6 +18,10 @@ Install #. Open your ``openmw.cfg`` file in your preferred plain text editor. It is located as described in :doc:`paths` and *not* in your OpenMW root directory. #. Find or search for ``data=``. This is located very near the bottom of the file. If you are using Morrowind, this first entry should already point to your Morrowind data directory, ``Data Files``; otherwise it will point to your game file, ``.omwgame``. #. Create a new line underneath and type: ``data="path/to/your/data folder"`` Remember, the *data folder* is where your mod's plugin files are. The double quotes around this path name are *required*. + +.. note:: + Some text editors, such as TextEdit on Mac, will autocorrect your double quotes to typographical "curly" quotes instead of leaving them as the propper neutral vertical quotes ``""``. + #. Save your ``openmw.cfg`` file. You have now installed your mod. Any simple replacer mods that only contain resource files such as meshes or textures will now automatically be loaded in the order of their ``data=*`` entry. This is important to note because replacer mods that replace the same resource will overwrite previous ones as you go down the list. From c10585fb07f96e7b597e1ab45eb6ad332f6febb1 Mon Sep 17 00:00:00 2001 From: Allofich Date: Wed, 11 Jan 2017 22:09:26 +0900 Subject: [PATCH 71/77] Match best attack selection to original MW (Fixes #3721) --- apps/openmw/mwmechanics/character.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c771d9fcca..297cb0804a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -70,12 +70,14 @@ std::string getBestAttack (const ESM::Weapon* weapon) int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; - if (slash >= chop && slash >= thrust) + if (slash == chop && slash == thrust) return "slash"; - else if (chop >= slash && chop >= thrust) - return "chop"; - else + else if (thrust >= chop && thrust >= slash) return "thrust"; + else if (slash >= chop && slash >= thrust) + return "slash"; + else + return "chop"; } // Converts a movement Run state to its equivalent Walk state. From 2322ab3125f5e238b18f0cbfd05c2e3767c9e2e7 Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 12 Jan 2017 00:07:22 +0900 Subject: [PATCH 72/77] Don't make Command spells cancel AI packages (Fixes #3649) --- apps/openmw/mwmechanics/aifollow.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index e46205b951..051a4a2ce1 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -37,6 +37,7 @@ namespace MWMechanics MWWorld::Ptr getTarget() const; virtual bool sideWithTarget() const { return true; } virtual bool followTargetThroughDoors() const { return true; } + virtual bool shouldCancelPreviousAi() const { return !mCommanded; } virtual AiFollow *clone() const; From 69ce9f32bc6dd90d9df026ac0691d87761571fc1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jan 2017 00:10:43 +0100 Subject: [PATCH 73/77] Activate/OnActivate fix (Bug #3712) --- apps/openmw/mwworld/refdata.cpp | 41 +++++++++++++++------------------ 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index de26d1292a..ae36db1c3d 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -13,7 +13,8 @@ namespace enum RefDataFlags { Flag_SuppressActivate = 1, // If set, activation will be suppressed and redirected to the OnActivate flag, which can then be handled by a script. - Flag_OnActivate = 2 + Flag_OnActivate = 2, + Flag_ActivationBuffered = 4 }; } @@ -241,38 +242,32 @@ namespace MWWorld return mChanged || !mAnimationState.empty(); } + bool RefData::activateByScript() + { + bool ret = (mFlags & Flag_ActivationBuffered); + mFlags &= ~(Flag_SuppressActivate|Flag_OnActivate); + return ret; + } + bool RefData::activate() { - if (!(mFlags & Flag_SuppressActivate)) - return true; + if (mFlags & Flag_SuppressActivate) + { + mFlags |= Flag_OnActivate|Flag_ActivationBuffered; + return false; + } else { - mFlags |= Flag_OnActivate; - return false; + return true; } } bool RefData::onActivate() { + bool ret = mFlags & Flag_OnActivate; mFlags |= Flag_SuppressActivate; - - if (mFlags & Flag_OnActivate) - { - mFlags &= (~Flag_OnActivate); - return true; - } - return false; - } - - bool RefData::activateByScript() - { - if (mFlags & Flag_SuppressActivate) - { - mFlags &= (~Flag_SuppressActivate); - return true; - } - else - return false; + mFlags &= (~Flag_OnActivate); + return ret; } const ESM::AnimationState& RefData::getAnimationState() const From b87abb0456f1bd26327cba8f8274ca158026cc9f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jan 2017 15:29:36 +0100 Subject: [PATCH 74/77] Reset activation flags when an object is copied (Fixes #3671, Fixes #3719) --- apps/openmw/mwworld/refdata.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index ae36db1c3d..f6fa3556fa 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -84,6 +84,7 @@ namespace MWWorld try { copy (refData); + mFlags &= ~(Flag_SuppressActivate|Flag_OnActivate|Flag_ActivationBuffered); } catch (...) { From a46c4de918853f6d539815c2341a5d0ffd4fd675 Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 12 Jan 2017 00:32:36 +0900 Subject: [PATCH 75/77] Stop combat when adding an AI package to an actor (Fixes #3722) --- apps/openmw/mwmechanics/aisequence.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index a134fd44ad..1bd2f98b3a 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -281,6 +281,10 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor) } } + // Stop combat when a non-combat AI package is added + if (isActualAiPackage(package.getTypeId())) + stopCombat(); + // remove previous packages if required if (package.shouldCancelPreviousAi()) { From e825010107a0d8773791cd3bd952d611665ff2da Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 12 Jan 2017 02:23:03 +0900 Subject: [PATCH 76/77] Apply command spell effects on impact Command spells should apply their effects, including taking an actor out of combat, every time a spell successfully hits, even if a previous command effect is still active. --- apps/openmw/mwmechanics/actors.cpp | 9 ++------- apps/openmw/mwmechanics/spellcasting.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index cd8f187775..c25eafcafb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -93,6 +93,7 @@ public: } }; +// Check for command effects having ended and remove package if necessary void adjustCommandedActor (const MWWorld::Ptr& actor) { CheckActorCommanded check(actor); @@ -112,13 +113,7 @@ void adjustCommandedActor (const MWWorld::Ptr& actor) } } - if (check.mCommanded && !hasCommandPackage) - { - // FIXME: don't use refid string - MWMechanics::AiFollow package("player", true); - stats.getAiSequence().stack(package, actor); - } - else if (!check.mCommanded && hasCommandPackage) + if (!check.mCommanded && hasCommandPackage) { stats.getAiSequence().erase(it); } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 489ab83ec1..242c5e659a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -27,6 +27,7 @@ #include "magiceffects.hpp" #include "npcstats.hpp" #include "actorutil.hpp" +#include "aifollow.hpp" namespace MWMechanics { @@ -490,6 +491,15 @@ namespace MWMechanics appliedLastingEffects.push_back(effect); + // Command spells should have their effect, including taking the target out of combat, each time the spell successfully affects the target + if (effectIt->mEffectID == ESM::MagicEffect::CommandHumanoid && target.getClass().isNpc() + || (effectIt->mEffectID == ESM::MagicEffect::CommandCreature && target.getTypeName() == typeid(ESM::Creature).name()) + && caster == getPlayer() && magnitude >= target.getClass().getCreatureStats(target).getLevel()) + { + MWMechanics::AiFollow package(caster.getCellRef().getRefId(), true); + target.getClass().getCreatureStats(target).getAiSequence().stack(package, target); + } + // For absorb effects, also apply the effect to the caster - but with a negative // magnitude, since we're transferring stats from the target to the caster if (!caster.isEmpty() && caster.getClass().isActor()) From f2240dde9cbbadce39fc5078562692dc4d227e07 Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 12 Jan 2017 03:11:45 +0900 Subject: [PATCH 77/77] Allow command spells to work when cast by AI on AI (Fixes #3723) --- apps/openmw/mwmechanics/actors.cpp | 6 ++---- apps/openmw/mwmechanics/spellcasting.cpp | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c25eafcafb..db8598a755 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -84,10 +84,8 @@ public: const std::string& sourceName, const std::string& sourceId, int casterActorId, float magnitude, float remainingTime = -1, float totalTime = -1) { - MWWorld::Ptr player = MWMechanics::getPlayer(); - if ( ((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc()) - || (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name())) - && casterActorId == player.getClass().getCreatureStats(player).getActorId() + if (((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc()) + || (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name())) && magnitude >= mActor.getClass().getCreatureStats(mActor).getLevel()) mCommanded = true; } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 242c5e659a..a364f90a96 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -492,9 +492,9 @@ namespace MWMechanics appliedLastingEffects.push_back(effect); // Command spells should have their effect, including taking the target out of combat, each time the spell successfully affects the target - if (effectIt->mEffectID == ESM::MagicEffect::CommandHumanoid && target.getClass().isNpc() - || (effectIt->mEffectID == ESM::MagicEffect::CommandCreature && target.getTypeName() == typeid(ESM::Creature).name()) - && caster == getPlayer() && magnitude >= target.getClass().getCreatureStats(target).getLevel()) + if (((effectIt->mEffectID == ESM::MagicEffect::CommandHumanoid && target.getClass().isNpc()) + || (effectIt->mEffectID == ESM::MagicEffect::CommandCreature && target.getTypeName() == typeid(ESM::Creature).name())) + && !caster.isEmpty() && caster.getClass().isActor() && target != getPlayer() && magnitude >= target.getClass().getCreatureStats(target).getLevel()) { MWMechanics::AiFollow package(caster.getCellRef().getRefId(), true); target.getClass().getCreatureStats(target).getAiSequence().stack(package, target);