Merge upstream.

pull/541/head
AnyOldName3 7 years ago
commit 44b2cf2b7f

@ -15,6 +15,7 @@ Programmers
Adam Hogan (aurix)
Aesylwinn
aegis
AHSauge
Aleksandar Jovanov
Alex Haddad (rainChu)
Alex McKibben
@ -41,6 +42,7 @@ Programmers
Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam)
crussell187
DanielVukelich
darkf
devnexen
Dieho
@ -155,6 +157,8 @@ Programmers
terrorfisch
thegriglat
Thomas Luppi (Digmaster)
tri4ng1e
unelsson
Will Herrmann (Thunderforge)
Tom Mason (wheybags)
Torben Leif Carrington (TorbenC)
@ -224,7 +228,7 @@ Artwork
Necrod - OpenMW Logo
Mickey Lyle (raevol) - Wordpress Theme
Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons
Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel, Lamoot - OpenMW Editor Icons
Inactive Contributors
---------------------

@ -303,25 +303,25 @@ if [ -z $SKIP_DOWNLOAD ]; then
# Boost
if [ -z $APPVEYOR ]; then
download "Boost 1.61.0" \
"http://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
"https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
"boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe"
fi
# Bullet
download "Bullet 2.86" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \
"Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z"
# FFmpeg
download "FFmpeg 3.2.4" \
"http://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \
"ffmpeg-3.2.4-win${BITS}.zip" \
"http://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \
"https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \
"ffmpeg-3.2.4-dev-win${BITS}.zip"
# MyGUI
download "MyGUI 3.2.2" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \
"MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z"
# OpenAL
@ -331,7 +331,7 @@ if [ -z $SKIP_DOWNLOAD ]; then
# OSG
download "OpenSceneGraph 3.4.1-scrawl" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
"OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z"
# Qt
@ -343,9 +343,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
fi
download "Qt 5.7.2" \
"http://download.qt.io/official_releases/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
"https://download.qt.io/official_releases/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
"http://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"qt-5-install.qs"
fi
@ -613,8 +613,7 @@ fi
SUFFIX=""
fi
add_runtime_dlls "$(pwd)/bin/lib"{EGL,GLESv2}${SUFFIX}.dll \
"$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done.

@ -135,6 +135,8 @@ if (WIN32)
endif()
# Dependencies
find_package(OpenGL REQUIRED)
if (USE_QT)
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
@ -182,12 +184,6 @@ if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind insta
set(OPENMW_USE_UNSHIELD TRUE)
endif()
option(OPENGL_ES "enable opengl es support" FALSE )
if (OPENGL_ES)
add_definitions(-DOPENGL_ES)
endif(OPENGL_ES)
# Fix for not visible pthreads functions for linker with glibc 2.15
if (UNIX AND NOT APPLE)
find_package (Threads)
@ -505,8 +501,8 @@ if(WIN32)
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md")
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_HELP_LINK "https:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_URL_INFO_ABOUT "https:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
SET(CPACK_NSIS_MUI_FINISHPAGE_RUN "openmw-launcher.exe")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
@ -549,11 +545,6 @@ endif()
# Components
add_subdirectory (components)
# Plugins
#if (BUILD_MYGUI_PLUGIN)
# add_subdirectory(plugins/mygui_resource_plugin)
#endif()
# Apps and tools
if (BUILD_OPENMW)
add_subdirectory( apps/openmw )

@ -3,15 +3,14 @@ How to contribute to OpenMW
Not sure what to do with all your free time? Pick out a task from here:
http://bugs.openmw.org/
https://bugs.openmw.org/
Currently, we are focused on completing the MW game experience and general polishing. Features out of this scope may be approved in some cases, but you should probably start a discussion first.
Note:
- Tasks set to 'openmw-future' are usually out of the current scope of the project and can't be started yet.
- Bugs that are not 'Confirmed' should be confirmed first.
- Larger Features should have a discussion before you start implementing.
- In many cases, it's best to have a discussion about possible solutions before you jump into coding.
* Tasks set to 'openmw-future' are usually out of the current scope of the project and can't be started yet.
* Bugs that are not 'Confirmed' should be confirmed first.
* Often, it's best to start a discussion about possible solutions before you jump into coding, especially for larger features.
Aside from coding, you can also help by triaging the issues list. Check for bugs that are 'Unconfirmed' and try to confirm them on your end, working out any details that may be necessary. Check for bugs that do not conform to [Bug reporting guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) and improve them to do so!
@ -20,20 +19,21 @@ There are various [Tools](https://wiki.openmw.org/index.php?title=Tools) to faci
Pull Request Guidelines
=======================
Thought of a change? Great! To facilitate the review process, your pull request description should include the following (if applicable):
To facilitate the review process, your pull request description should include the following, if applicable:
* A link back to the bug report or forum discussion that prompted the change
* A link back to the bug report or forum discussion that prompted the change. Note: when linking bugs, use the syntax ```[Bug #xyz](https://bugs.openmw.org/issues/#xyz)``` to create a clickable link. Writing only 'Bug #xyz' will unfortunately create a link to the Github pull request with that number instead.
* Summary of the changes made
* Reasoning / motivation behind the change
* What testing you have carried out to verify the change
Furthermore, we advise to:
* Separate your work into multiple pull requests whenever possible. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.
* Avoid stuffing unrelated commits into one pull request. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).
* Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway.
* Reference the bug / feature ticket(s) in your commit message (e.g. 'Bug #123') to make it easier to keep track of what we changed for what reason. Our bugtracker will show those commits next to the ticket. If your commit message includes 'Fixes #123', that bug/feature will automatically be set to 'Closed' when your commit is merged.
* When pulling changes from master, prefer rebase over merge. Consider using a merge if there are conflicts or for long-running PRs.
Guidelines for original engine "fixes"
=================================
@ -47,7 +47,7 @@ Unfortunately, the definition of what is a "bug" is not so clear. Consider that
* Many people will actually <i>like</i> these "bugs" because that is what they remember the game for.
* Exploits may be part of the fun of an open-world game - they reward knowledge with power. There are too many of them to plug them all, anyway.
OpenMW, in its default configuration, is meant to be a faithful reimplementation of Morrowind, minus things like crash bugs, stability issues and design errors. However, we try to avoid touching anything that affects the core gameplay, the balancing of the game or introduces incompatibilities with existing mod content.
OpenMW, in its default configuration, is meant to be a faithful reimplementation of Morrowind, minus things like crash bugs, stability issues and severe design errors. However, we try to avoid touching anything that affects the core gameplay, the balancing of the game or introduces incompatibilities with existing mod content.
That said, we may sometimes evaluate such issues on an individual basis. Common exceptions to the above would be:
@ -62,10 +62,54 @@ We get it, you have waited so long for feature XYZ to be available in Morrowind
Unfortunately, since maintaining features comes at a cost and our resources are limited, we have to be a little selective in what features we allow into the main repository. Generally:
- Features should be as generic and non-redundant as possible.
- Any feature that is also possible with modding should be done as a mod instead.
- In the future, OpenMW plans to expand the scope of what is possible with modding, e.g. by moving certain game logic into editable scripts.
- Currently, modders can edit OpenMW's GUI skins and layout XML files, although there are still a few missing hooks (e.g. scripting support) in order to make this into a powerful way of modding.
- If a feature introduces new game UI strings, that reduces its chance of being accepted because we do not currently have any way of localizing these to the user's Morrowind installation language.
* Features should be as generic and non-redundant as possible.
* Any feature that is also possible with modding should be done as a mod instead.
* In the future, OpenMW plans to expand the scope of what is possible with modding, e.g. by moving certain game logic into editable scripts.
* Currently, modders can edit OpenMW's GUI skins and layout XML files, although there are still a few missing hooks (e.g. scripting support) in order to make this into a powerful way of modding.
* If a feature introduces new game UI strings, that reduces its chance of being accepted because we do not currently have any way of localizing these to the user's Morrowind installation language.
If you are in doubt of your feature being within our scope, it is probably best to start a forum discussion first. See the [settings documentation](https://openmw.readthedocs.io/en/stable/reference/modding/settings/index.html) and [Features list](https://wiki.openmw.org/index.php?title=Features) for some examples of features that were deemed acceptable.
Reviewing pull requests
=======================
We welcome any help in reviewing open PRs. You don't need to be a developer to comment on new features. We also encourage ["junior" developers to review senior's work](https://pagefault.blog/2018/04/08/why-junior-devs-should-review-seniors-commits/).
This review process is divided into two sections because complaining about code or style issues hardly makes sense until the functionality of the PR is deemed OK. Anyone can help with the Functionality Review while most parts of the Code Review require you to have programming experience.
In addition to the checklist below, make sure to check that the Pull Request Guidelines (first half of this document) were followed.
First review
============
1. Ask for missing information or clarifications. Compare against the project's design goals and roadmap.
2. Check if the automated tests are passing. If they are not, make the PR author aware of the issue and potentially link to the error line on Travis CI or Appveyor. If the error appears unrelated to the PR and/or the master branch is failing with the same error, our CI has broken and needs to be fixed independently of any open PRs. Raise this issue on the forums, bug tracker or with the relevant maintainer. The PR can be merged in this case as long as you've built it yourself to make sure it does build.
3. Make sure that the new code has been tested thoroughly, either by asking the author or, preferably, testing yourself. In a complex project like OpenMW, it is easy to make mistakes, typos, etc. Therefore, prefer testing all code changes, no matter how trivial they look. When you have tested a PR that no one has tested so far, post a comment letting us know.
4. On long running PRs, request the author to update its description with the current state or a checklist of things left to do.
Code Review
===========
1. Carefully review each line for issues the author may not have thought of, paying special attention to 'special' cases. Often, people build their code with a particular mindset and forget about other configurations or unexpected interactions.
2. If any changes are workarounds for an issue in an upstream library, make sure the issue was reported upstream so we can eventually drop the workaround when the issue is fixed and the new version of that library is a build dependency.
3. Make sure PRs do not turn into arguments about hardly related issues. If the PR author disagrees with an established part of the project (e.g. supported build environments), they should open a forum discussion or bug report and in the meantime adjust the PR to adhere to the established way, rather than leaving the PR hanging on a dispute.
4. Check if the code matches our style guidelines.
5. Check to make sure the commit history is clean. Squashing should be considered if the review process has made the commit history particularly long. Commits that don't build should be avoided because they are a nuisance for ```git bisect```.
Merging
=======
To be able to merge PRs, commit priviledges are required. If you do not have the priviledges, just ping someone that does have them with a short comment like "Looks good to me @user".
The person to merge the PR may either use github's Merge button or if using the command line, use the ```--no-ff``` flag (so a merge commit is created, just like with Github's merge button) and include the pull request number in the commit description.
Dealing with regressions
========================
The master branch should always be in a working state that is not worse than the previous release in any way. If a regression is found, the first and foremost priority should be to get the regression fixed quickly, either by reverting the change that caused it or finding a better solution. Please avoid leaving the project in the 'broken' state for an extensive period of time while proper solutions are found. If the solution takes more than a day or so then it is usually better to revert the offending change first and reapply it later when fixed.
Other resources
===============
[GitHub blog - how to write the perfect pull request](https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/)

@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/philosophy/why-not-lgpl.html>.

@ -9,7 +9,7 @@ OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction
* Version: 0.43.0
* License: GPLv3 (see [LICENSE](https://github.com/OpenMW/openmw/blob/master/LICENSE) for more information)
* Website: http://www.openmw.org
* Website: https://www.openmw.org
* IRC: #openmw on irc.freenode.net
Font Licenses:
@ -30,8 +30,8 @@ Getting Started
* [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup)
* [Testing the game](https://wiki.openmw.org/index.php?title=Testing)
* [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted)
* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug!
* [Known issues](http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker)
* [Report a bug](https://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug!
* [Known issues](https://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker)
The data path
-------------

@ -694,7 +694,7 @@ void Record<ESM::Dialogue>::print()
// loads, rather than loading and then dumping. :-( Anyone mind if
// I change this?
ESM::Dialogue::InfoContainer::iterator iit;
for (iit = mData.mInfo.begin(); iit != mData.mInfo.end(); iit++)
for (iit = mData.mInfo.begin(); iit != mData.mInfo.end(); ++iit)
std::cout << "INFO!" << iit->mId << std::endl;
}
@ -1040,45 +1040,47 @@ void Record<ESM::NPC>::print()
if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
std::cout << " Level: " << mData.mNpdt12.mLevel << std::endl;
std::cout << " Reputation: " << (int)mData.mNpdt12.mReputation << std::endl;
std::cout << " Disposition: " << (int)mData.mNpdt12.mDisposition << std::endl;
std::cout << " Rank: " << (int)mData.mNpdt12.mRank << std::endl;
std::cout << " Unknown1: "
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown1) << std::endl;
std::cout << " Unknown2: "
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl;
std::cout << " Unknown3: "
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl;
std::cout << " Gold: " << mData.mNpdt12.mGold << std::endl;
std::cout << " Level: " << mData.mNpdt.mLevel << std::endl;
std::cout << " Reputation: " << (int)mData.mNpdt.mReputation << std::endl;
std::cout << " Disposition: " << (int)mData.mNpdt.mDisposition << std::endl;
std::cout << " Rank: " << (int)mData.mNpdt.mRank << std::endl;
//Why do we want to print these fields? They are padding in the struct and contain
// nothing of real value. Now we don't deal with NPDTstruct12 in runtime either...
//std::cout << " Unknown1: "
// << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown1) << std::endl;
//std::cout << " Unknown2: "
// << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl;
//std::cout << " Unknown3: "
// << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl;
std::cout << " Gold: " << mData.mNpdt.mGold << std::endl;
}
else {
std::cout << " Level: " << mData.mNpdt52.mLevel << std::endl;
std::cout << " Reputation: " << (int)mData.mNpdt52.mReputation << std::endl;
std::cout << " Disposition: " << (int)mData.mNpdt52.mDisposition << std::endl;
std::cout << " Rank: " << (int)mData.mNpdt52.mRank << std::endl;
std::cout << " FactionID: " << (int)mData.mNpdt52.mFactionID << std::endl;
std::cout << " Level: " << mData.mNpdt.mLevel << std::endl;
std::cout << " Reputation: " << (int)mData.mNpdt.mReputation << std::endl;
std::cout << " Disposition: " << (int)mData.mNpdt.mDisposition << std::endl;
std::cout << " Rank: " << (int)mData.mNpdt.mRank << std::endl;
std::cout << " FactionID: " << (int)mData.mNpdt.mFactionID << std::endl;
std::cout << " Attributes:" << std::endl;
std::cout << " Strength: " << (int)mData.mNpdt52.mStrength << std::endl;
std::cout << " Intelligence: " << (int)mData.mNpdt52.mIntelligence << std::endl;
std::cout << " Willpower: " << (int)mData.mNpdt52.mWillpower << std::endl;
std::cout << " Agility: " << (int)mData.mNpdt52.mAgility << std::endl;
std::cout << " Speed: " << (int)mData.mNpdt52.mSpeed << std::endl;
std::cout << " Endurance: " << (int)mData.mNpdt52.mEndurance << std::endl;
std::cout << " Personality: " << (int)mData.mNpdt52.mPersonality << std::endl;
std::cout << " Luck: " << (int)mData.mNpdt52.mLuck << std::endl;
std::cout << " Strength: " << (int)mData.mNpdt.mStrength << std::endl;
std::cout << " Intelligence: " << (int)mData.mNpdt.mIntelligence << std::endl;
std::cout << " Willpower: " << (int)mData.mNpdt.mWillpower << std::endl;
std::cout << " Agility: " << (int)mData.mNpdt.mAgility << std::endl;
std::cout << " Speed: " << (int)mData.mNpdt.mSpeed << std::endl;
std::cout << " Endurance: " << (int)mData.mNpdt.mEndurance << std::endl;
std::cout << " Personality: " << (int)mData.mNpdt.mPersonality << std::endl;
std::cout << " Luck: " << (int)mData.mNpdt.mLuck << std::endl;
std::cout << " Skills:" << std::endl;
for (int i = 0; i != ESM::Skill::Length; i++)
std::cout << " " << skillLabel(i) << ": "
<< (int)(mData.mNpdt52.mSkills[i]) << std::endl;
<< (int)(mData.mNpdt.mSkills[i]) << std::endl;
std::cout << " Health: " << mData.mNpdt52.mHealth << std::endl;
std::cout << " Magicka: " << mData.mNpdt52.mMana << std::endl;
std::cout << " Fatigue: " << mData.mNpdt52.mFatigue << std::endl;
std::cout << " Unknown: " << (int)mData.mNpdt52.mUnknown << std::endl;
std::cout << " Gold: " << mData.mNpdt52.mGold << std::endl;
std::cout << " Health: " << mData.mNpdt.mHealth << std::endl;
std::cout << " Magicka: " << mData.mNpdt.mMana << std::endl;
std::cout << " Fatigue: " << mData.mNpdt.mFatigue << std::endl;
std::cout << " Unknown: " << (int)mData.mNpdt.mUnknown << std::endl;
std::cout << " Gold: " << mData.mNpdt.mGold << std::endl;
}
std::vector<ESM::ContItem>::iterator cit;
@ -1123,7 +1125,7 @@ void Record<ESM::Pathgrid>::print()
int i = 0;
ESM::Pathgrid::PointList::iterator pit;
for (pit = mData.mPoints.begin(); pit != mData.mPoints.end(); pit++)
for (pit = mData.mPoints.begin(); pit != mData.mPoints.end(); ++pit)
{
std::cout << " Point[" << i << "]:" << std::endl;
std::cout << " Coordinates: (" << pit->mX << ","
@ -1135,7 +1137,7 @@ void Record<ESM::Pathgrid>::print()
}
i = 0;
ESM::Pathgrid::EdgeList::iterator eit;
for (eit = mData.mEdges.begin(); eit != mData.mEdges.end(); eit++)
for (eit = mData.mEdges.begin(); eit != mData.mEdges.end(); ++eit)
{
std::cout << " Edge[" << i << "]: " << eit->mV0 << " -> " << eit->mV1 << std::endl;
if (eit->mV0 >= mData.mData.mS2 || eit->mV1 >= mData.mData.mS2)

@ -122,7 +122,7 @@ public:
}
else
{
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt.mLevel;
mContext->mPlayerBase = npc;
ESM::SpellState::SpellParams empty;
// FIXME: player start spells and birthsign spells aren't listed here,

@ -377,7 +377,7 @@ namespace ESSImport
profile.mPlayerClassName = context.mCustomPlayerClassName;
else
profile.mPlayerClassId = context.mPlayerBase.mClass;
profile.mPlayerLevel = context.mPlayerBase.mNpdt52.mLevel;
profile.mPlayerLevel = context.mPlayerBase.mNpdt.mLevel;
profile.mPlayerName = header.mGameData.mPlayerName.toString();
writeScreenshot(header, profile);

@ -106,7 +106,7 @@ if (DESIRED_QT_VERSION MATCHES 4)
target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY})
endif(WIN32)
else()
qt5_use_modules(openmw-launcher Widgets Core)
target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)
endif()
if (BUILD_WITH_CODE_COVERAGE)

@ -35,9 +35,11 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
loadSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
// Other Settings
// Saves Settings
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
// Other Settings
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
screenshotFormatComboBox->addItem(screenshotFormatString);
@ -69,9 +71,14 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
saveSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
// Other Settings
// Saves Settings
saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
int maximumQuicksaves = maximumQuicksavesComboBox->value();
if (maximumQuicksaves != mEngineSettings.getInt("max quicksaves", "Saves")) {
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
}
// Other Settings
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
mEngineSettings.setString("screenshot format", "General", screenshotFormatString);

@ -33,12 +33,12 @@ bool hasExtension(std::string filename, std::string extensionToFind)
}
///See if the file has the "nif" extension.
bool isNIF(std::string filename)
bool isNIF(const std::string & filename)
{
return hasExtension(filename,"nif");
}
///See if the file has the "bsa" extension.
bool isBSA(std::string filename)
bool isBSA(const std::string & filename)
{
return hasExtension(filename,"bsa");
}

@ -246,7 +246,7 @@ if (DESIRED_QT_VERSION MATCHES 4)
target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY})
endif()
else()
qt5_use_modules(openmw-cs Widgets Core Network OpenGL)
target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
endif()
if (WIN32)

@ -579,7 +579,7 @@ bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined)
}
// We do not use isString() here, because there could be a pre-defined filter with an ID that is
// equal a filter keyword.
else if (token.mType==Token::Type_String && allowPredefined)
else if (token.mType==Token::Type_String)
{
if (getNextToken()!=Token (Token::Type_EOS))
{

@ -49,7 +49,7 @@ namespace CSMPrefs
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
if (shortcutListIt != mWidgetShortcuts.end())
{
std::remove(shortcutListIt->second.begin(), shortcutListIt->second.end(), shortcut);
shortcutListIt->second.erase(std::remove(shortcutListIt->second.begin(), shortcutListIt->second.end(), shortcut), shortcutListIt->second.end());
}
}

@ -32,9 +32,6 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
if (class_.mName.empty())
messages.push_back (std::make_pair (id, class_.mId + " has an empty name"));
if (class_.mDescription.empty())
messages.push_back (std::make_pair (id, class_.mId + " has an empty description"));
// test for invalid attributes
for (int i=0; i<2; ++i)
if (class_.mData.mAttribute[i]==-1)

@ -29,7 +29,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId);
// check the number of pathgrid points
if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
if (pathgrid.mData.mS2 < static_cast<int>(pathgrid.mPoints.size()))
messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error);
else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error);

@ -239,9 +239,7 @@ void CSMTools::ReferenceableCheckStage::bookCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Book& book = (dynamic_cast<const CSMWorld::Record<ESM::Book>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId);
@ -260,9 +258,7 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId);
@ -283,9 +279,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Potion& potion = (dynamic_cast<const CSMWorld::Record<ESM::Potion>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId);
@ -306,9 +300,7 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Apparatus& apparatus = (dynamic_cast<const CSMWorld::Record<ESM::Apparatus>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, apparatus.mId);
@ -329,9 +321,7 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Armor& armor = (dynamic_cast<const CSMWorld::Record<ESM::Armor>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, armor.mId);
@ -358,9 +348,7 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId);
@ -378,9 +366,7 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId);
@ -512,9 +498,7 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, ingredient.mId);
@ -577,13 +561,8 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
messages.push_back (std::make_pair (id, light.mId + " has negative light radius"));
if (light.mData.mFlags & ESM::Light::Carry)
{
inventoryItemCheck<ESM::Light>(light, messages, id.toString());
if (light.mData.mTime == 0)
messages.push_back (std::make_pair (id, light.mId + " has zero duration"));
}
// Check that mentioned scripts exist
scriptCheck<ESM::Light>(light, messages, id.toString());
}
@ -596,9 +575,7 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Lockpick& lockpick = (dynamic_cast<const CSMWorld::Record<ESM::Lockpick>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, lockpick.mId);
@ -619,9 +596,7 @@ void CSMTools::ReferenceableCheckStage::miscCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Miscellaneous& miscellaneous = (dynamic_cast<const CSMWorld::Record<ESM::Miscellaneous>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId);
@ -644,12 +619,12 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
const ESM::NPC& npc = (dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(baseRecord)).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Npc, npc.mId);
short level(npc.mNpdt52.mLevel);
char disposition(npc.mNpdt52.mDisposition);
char reputation(npc.mNpdt52.mReputation);
char rank(npc.mNpdt52.mRank);
short level(npc.mNpdt.mLevel);
char disposition(npc.mNpdt.mDisposition);
char reputation(npc.mNpdt.mReputation);
char rank(npc.mNpdt.mRank);
//Don't know what unknown is for
int gold(npc.mNpdt52.mGold);
int gold(npc.mNpdt.mGold);
//Detect if player is present
if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl?
@ -663,36 +638,36 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
return;
}
level = npc.mNpdt12.mLevel;
disposition = npc.mNpdt12.mDisposition;
reputation = npc.mNpdt12.mReputation;
rank = npc.mNpdt12.mRank;
gold = npc.mNpdt12.mGold;
level = npc.mNpdt.mLevel;
disposition = npc.mNpdt.mDisposition;
reputation = npc.mNpdt.mReputation;
rank = npc.mNpdt.mRank;
gold = npc.mNpdt.mGold;
}
else
{
if (npc.mNpdt52.mAgility == 0)
if (npc.mNpdt.mAgility == 0)
messages.push_back (std::make_pair (id, npc.mId + " agility has zero value"));
if (npc.mNpdt52.mEndurance == 0)
if (npc.mNpdt.mEndurance == 0)
messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value"));
if (npc.mNpdt52.mIntelligence == 0)
if (npc.mNpdt.mIntelligence == 0)
messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value"));
if (npc.mNpdt52.mLuck == 0)
if (npc.mNpdt.mLuck == 0)
messages.push_back (std::make_pair (id, npc.mId + " luck has zero value"));
if (npc.mNpdt52.mPersonality == 0)
if (npc.mNpdt.mPersonality == 0)
messages.push_back (std::make_pair (id, npc.mId + " personality has zero value"));
if (npc.mNpdt52.mStrength == 0)
if (npc.mNpdt.mStrength == 0)
messages.push_back (std::make_pair (id, npc.mId + " strength has zero value"));
if (npc.mNpdt52.mSpeed == 0)
if (npc.mNpdt.mSpeed == 0)
messages.push_back (std::make_pair (id, npc.mId + " speed has zero value"));
if (npc.mNpdt52.mWillpower == 0)
if (npc.mNpdt.mWillpower == 0)
messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value"));
}
@ -706,22 +681,14 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
messages.push_back (std::make_pair (id, npc.mId + " has any empty name"));
if (npc.mClass.empty())
{
messages.push_back (std::make_pair (id, npc.mId + " has any empty class"));
}
messages.push_back (std::make_pair (id, npc.mId + " has an empty class"));
else if (mClasses.searchId (npc.mClass) == -1)
{
messages.push_back (std::make_pair (id, npc.mId + " has invalid class"));
}
if (npc.mRace.empty())
{
messages.push_back (std::make_pair (id, npc.mId + " has any empty race"));
}
messages.push_back (std::make_pair (id, npc.mId + " has an empty race"));
else if (mRaces.searchId (npc.mRace) == -1)
{
messages.push_back (std::make_pair (id, npc.mId + " has invalid race"));
}
if (disposition < 0)
messages.push_back (std::make_pair (id, npc.mId + " has negative disposition"));
@ -823,7 +790,7 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
{
//checking of health
if (weapon.mData.mHealth <= 0)
messages.push_back (std::make_pair (id, weapon.mId + " has non-positivie health"));
messages.push_back (std::make_pair (id, weapon.mId + " has non-positive health"));
if (weapon.mData.mReach < 0)
messages.push_back (std::make_pair (id, weapon.mId + " has negative reach"));
@ -842,9 +809,7 @@ void CSMTools::ReferenceableCheckStage::probeCheck(
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
return;
}
const ESM::Probe& probe = (dynamic_cast<const CSMWorld::Record<ESM::Probe>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Probe, probe.mId);

@ -27,7 +27,7 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
if (sound.mData.mMinRange>sound.mData.mMaxRange)
messages.push_back (std::make_pair (id, "Maximum range larger than minimum range"));
messages.push_back (std::make_pair (id, "Minimum range larger than maximum range"));
/// \todo check, if the sound file exists
}

@ -61,12 +61,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
connect (&mVerifier, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
this, SLOT (verifierMessage (const CSMDoc::Message&, int)));
std::vector<std::string> mandatoryIds; // I want C++11, damn it!
mandatoryIds.push_back ("Day");
mandatoryIds.push_back ("DaysPassed");
mandatoryIds.push_back ("GameHour");
mandatoryIds.push_back ("Month");
mandatoryIds.push_back ("PCRace");
std::vector<std::string> mandatoryIds {"Day", "DaysPassed", "GameHour", "Month", "PCRace"};
mVerifierOperation->appendStage (new MandatoryIdStage (mData.getGlobals(),
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));

@ -66,7 +66,7 @@ namespace CSMWorld
{ ColumnId_SleepForbidden, "Sleep Forbidden" },
{ ColumnId_InteriorWater, "Interior Water" },
{ ColumnId_InteriorSky, "Interior Sky" },
{ ColumnId_Model, "Model" },
{ ColumnId_Model, "Model/Animation" },
{ ColumnId_Script, "Script" },
{ ColumnId_Icon, "Icon" },
{ ColumnId_Weight, "Weight" },

@ -962,6 +962,29 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
return mReader->getRecordCount();
}
void CSMWorld::Data::loadFallbackEntries()
{
// Load default marker definitions, if game files do not have them for some reason
std::pair<std::string, std::string> markers[] = {
std::make_pair("divinemarker", "marker_divine.nif"),
std::make_pair("doormarker", "marker_arrow.nif"),
std::make_pair("northmarker", "marker_north.nif"),
std::make_pair("templemarker", "marker_temple.nif"),
std::make_pair("travelmarker", "marker_travel.nif")
};
for (const std::pair<std::string, std::string> marker : markers)
{
if (mReferenceables.searchId (marker.first)==-1)
{
CSMWorld::Record<ESM::Static> record;
record.mBase = ESM::Static(marker.first, marker.second);
record.mState = CSMWorld::RecordBase::State_BaseOnly;
mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static);
}
}
}
bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
{
if (!mReader)
@ -983,6 +1006,9 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
mReader = 0;
mDialogue = 0;
loadFallbackEntries();
return true;
}

@ -144,6 +144,8 @@ namespace CSMWorld
static int count (RecordBase::State state, const CollectionBase& collection);
void loadFallbackEntries();
public:
Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,

@ -914,7 +914,7 @@ void CSMWorld::NpcAttributesRefIdAdapter::setNestedTable (const RefIdColumn* col
ESM::NPC npc = record.get();
// store the whole struct
npc.mNpdt52 =
npc.mNpdt =
static_cast<const NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);
record.setModified (npc);
@ -928,7 +928,7 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTab
// return the whole struct
std::vector<ESM::NPC::NPDTstruct52> wrap;
wrap.push_back(record.get().mNpdt52);
wrap.push_back(record.get().mNpdt);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> >(wrap);
}
@ -939,7 +939,7 @@ QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52;
const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt;
if (subColIndex == 0)
return subRowIndex;
@ -966,7 +966,7 @@ void CSMWorld::NpcAttributesRefIdAdapter::setNestedData (const RefIdColumn *colu
Record<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52;
ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt;
if (subColIndex == 1)
switch(subRowIndex)
@ -1021,7 +1021,7 @@ void CSMWorld::NpcSkillsRefIdAdapter::setNestedTable (const RefIdColumn* column,
ESM::NPC npc = record.get();
// store the whole struct
npc.mNpdt52 =
npc.mNpdt =
static_cast<const NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);
record.setModified (npc);
@ -1035,7 +1035,7 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (
// return the whole struct
std::vector<ESM::NPC::NPDTstruct52> wrap;
wrap.push_back(record.get().mNpdt52);
wrap.push_back(record.get().mNpdt);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::NPC::NPDTstruct52> >(wrap);
}
@ -1046,7 +1046,7 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52;
const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt;
if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length)
throw std::runtime_error ("index out of range");
@ -1065,7 +1065,7 @@ void CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column,
Record<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52;
ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt;
if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length)
throw std::runtime_error ("index out of range");
@ -1130,30 +1130,30 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column
if (autoCalc)
switch (subColIndex)
{
case 0: return static_cast<int>(record.get().mNpdt12.mLevel);
case 0: return static_cast<int>(record.get().mNpdt.mLevel);
case 1: return QVariant(QVariant::UserType);
case 2: return QVariant(QVariant::UserType);
case 3: return QVariant(QVariant::UserType);
case 4: return QVariant(QVariant::UserType);
case 5: return static_cast<int>(record.get().mNpdt12.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt12.mReputation);
case 7: return static_cast<int>(record.get().mNpdt12.mRank);
case 8: return record.get().mNpdt12.mGold;
case 5: return static_cast<int>(record.get().mNpdt.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt.mReputation);
case 7: return static_cast<int>(record.get().mNpdt.mRank);
case 8: return record.get().mNpdt.mGold;
case 9: return record.get().mPersistent == true;
default: return QVariant(); // throw an exception here?
}
else
switch (subColIndex)
{
case 0: return static_cast<int>(record.get().mNpdt52.mLevel);
case 1: return static_cast<int>(record.get().mNpdt52.mFactionID);
case 2: return static_cast<int>(record.get().mNpdt52.mHealth);
case 3: return static_cast<int>(record.get().mNpdt52.mMana);
case 4: return static_cast<int>(record.get().mNpdt52.mFatigue);
case 5: return static_cast<int>(record.get().mNpdt52.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt52.mReputation);
case 7: return static_cast<int>(record.get().mNpdt52.mRank);
case 8: return record.get().mNpdt52.mGold;
case 0: return static_cast<int>(record.get().mNpdt.mLevel);
case 1: return static_cast<int>(record.get().mNpdt.mFactionID);
case 2: return static_cast<int>(record.get().mNpdt.mHealth);
case 3: return static_cast<int>(record.get().mNpdt.mMana);
case 4: return static_cast<int>(record.get().mNpdt.mFatigue);
case 5: return static_cast<int>(record.get().mNpdt.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt.mReputation);
case 7: return static_cast<int>(record.get().mNpdt.mRank);
case 8: return record.get().mNpdt.mGold;
case 9: return record.get().mPersistent == true;
default: return QVariant(); // throw an exception here?
}
@ -1171,30 +1171,30 @@ void CSMWorld::NpcMiscRefIdAdapter::setNestedData (const RefIdColumn *column,
if (autoCalc)
switch(subColIndex)
{
case 0: npc.mNpdt12.mLevel = static_cast<short>(value.toInt()); break;
case 0: npc.mNpdt.mLevel = static_cast<short>(value.toInt()); break;
case 1: return;
case 2: return;
case 3: return;
case 4: return;
case 5: npc.mNpdt12.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt12.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt12.mRank = static_cast<signed char>(value.toInt()); break;
case 8: npc.mNpdt12.mGold = value.toInt(); break;
case 5: npc.mNpdt.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt.mRank = static_cast<signed char>(value.toInt()); break;
case 8: npc.mNpdt.mGold = value.toInt(); break;
case 9: npc.mPersistent = value.toBool(); break;
default: return; // throw an exception here?
}
else
switch(subColIndex)
{
case 0: npc.mNpdt52.mLevel = static_cast<short>(value.toInt()); break;
case 1: npc.mNpdt52.mFactionID = static_cast<char>(value.toInt()); break;
case 2: npc.mNpdt52.mHealth = static_cast<unsigned short>(value.toInt()); break;
case 3: npc.mNpdt52.mMana = static_cast<unsigned short>(value.toInt()); break;
case 4: npc.mNpdt52.mFatigue = static_cast<unsigned short>(value.toInt()); break;
case 5: npc.mNpdt52.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt52.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt52.mRank = static_cast<signed char>(value.toInt()); break;
case 8: npc.mNpdt52.mGold = value.toInt(); break;
case 0: npc.mNpdt.mLevel = static_cast<short>(value.toInt()); break;
case 1: npc.mNpdt.mFactionID = static_cast<char>(value.toInt()); break;
case 2: npc.mNpdt.mHealth = static_cast<unsigned short>(value.toInt()); break;
case 3: npc.mNpdt.mMana = static_cast<unsigned short>(value.toInt()); break;
case 4: npc.mNpdt.mFatigue = static_cast<unsigned short>(value.toInt()); break;
case 5: npc.mNpdt.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt.mRank = static_cast<signed char>(value.toInt()); break;
case 8: npc.mNpdt.mGold = value.toInt(); break;
case 9: npc.mPersistent = value.toBool(); break;
default: return; // throw an exception here?
}
@ -1375,8 +1375,6 @@ QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn
return subRowIndex + 1;
else if (subColIndex < 3) // 1 or 2
return creature.mData.mAttack[(subRowIndex * 2) + (subColIndex - 1)];
else
return QVariant(); // throw an exception here?
}
void CSMWorld::CreatureAttackRefIdAdapter::setNestedData (const RefIdColumn *column,

@ -64,21 +64,21 @@ namespace
static const TypeData sIdArg[] =
{
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", ":./globvar.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":./GMST.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", ":./global-variable.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":./gmst.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", ":./skill.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", ":./class.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", ":./faction.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", ":./race.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", ":./sound.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", ":./script.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./region.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", ":./dialogue-topics.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", ":./journal-topics.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", ":./dialogue-topic-infos.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", ":./journal-topic-infos.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 },
@ -93,7 +93,7 @@ namespace
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList,
"Creature Levelled List", ":./creature.png" },
"Creature Levelled List", ":./leveled-creature.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList,
"Item Levelled List", ":./leveled-item.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" },
@ -109,20 +109,20 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", 0 },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", ":./enchantment.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.png" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":resources-mesh"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":resources-music" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":resources-sound" },
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":resources-texture"},
{ CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video"},
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", ":./sound-generator.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", ":./magic-effect.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":./land-heightmap.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", ":./land-texture.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":./pathgrid.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 },

@ -167,7 +167,7 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,
delete iter->second;
mDocuments.erase (iter);
}
else if (!completed && !error.empty())
else
{
iter->second->abort (error);
// Leave the window open for now (wait for the user to close it)

@ -106,7 +106,7 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
/// \todo remove this label once we are feature complete and convinced that this thing is
/// working properly.
QLabel *warning = new QLabel ("<font color=Red>WARNING: OpenMW-CS is in alpha stage.<p>The editor is not feature complete and not sufficiently tested.<br>In theory your data should be safe. But we strongly advice to make backups regularly if you are working with live data.</font color>");
QLabel *warning = new QLabel ("<font color=Red>WARNING: OpenMW-CS is in alpha stage.<p>The editor is not feature complete and not sufficiently tested.<br>In theory your data should be safe. But we strongly advise to make backups regularly if you are working with live data.</font color>");
QFont font;
font.setPointSize (12);

@ -92,7 +92,7 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos)
}
CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
: EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference | Mask_Terrain, "Instance editing",
: EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), Mask_Reference | Mask_Terrain, "Instance editing",
parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None),
mDragAxis (-1), mLocked (false), mUnitScaleDist(1)
{
@ -104,14 +104,14 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar)
{
mSubMode = new CSVWidget::SceneToolMode (toolbar, "Edit Sub-Mode");
mSubMode->addButton (new InstanceMoveMode (this), "move");
mSubMode->addButton (":placeholder", "rotate",
mSubMode->addButton (":scenetoolbar/transform-rotate", "rotate",
"Rotate selected instances"
"<ul><li>Use {scene-edit-primary} to rotate instances freely</li>"
"<li>Use {scene-edit-secondary} to rotate instances within the grid</li>"
"<li>The center of the view acts as the axis of rotation</li>"
"</ul>"
"<font color=Red>Grid rotate not implemented yet</font color>");
mSubMode->addButton (":placeholder", "scale",
mSubMode->addButton (":scenetoolbar/transform-scale", "scale",
"Scale selected instances"
"<ul><li>Use {scene-edit-primary} to scale instances freely</li>"
"<li>Use {scene-edit-secondary} to scale instances along the grid</li>"

@ -2,7 +2,7 @@
#include "instancemovemode.hpp"
CSVRender::InstanceMoveMode::InstanceMoveMode (QWidget *parent)
: ModeButton (QIcon (QPixmap (":placeholder")),
: ModeButton (QIcon (QPixmap (":scenetoolbar/transform-move")),
"Move selected instances"
"<ul><li>Use {scene-edit-primary} to move instances around freely</li>"
"<li>Use {scene-edit-secondary} to move instances around within the grid</li>"

@ -13,7 +13,7 @@ namespace CSVRender
, mWorldspaceWidget(worldspaceWidget)
, mInteractionMask(interactionMask)
{
addButton(":placeholder", "cube-centre",
addButton(":scenetoolbar/selection-mode-cube", "cube-centre",
"Centred cube"
"<ul><li>Drag with {scene-select-primary} (make instances the selection) or {scene-select-secondary} "
"(invert selection state) from the centre of the selection cube outwards</li>"
@ -22,7 +22,7 @@ namespace CSVRender
"starting on an instance will have the same effect</li>"
"</ul>"
"<font color=Red>Not implemented yet</font color>");
addButton(":placeholder", "cube-corner",
addButton(":scenetoolbar/selection-mode-cube-corner", "cube-corner",
"Cube corner to corner"
"<ul><li>Drag with {scene-select-primary} (make instances the selection) or {scene-select-secondary} "
"(invert selection state) from one corner of the selection cube to the opposite corner</li>"
@ -31,7 +31,7 @@ namespace CSVRender
"starting on an instance will have the same effect</li>"
"</ul>"
"<font color=Red>Not implemented yet</font color>");
addButton(":placeholder", "sphere",
addButton(":scenetoolbar/selection-mode-cube-sphere", "sphere",
"Centred sphere"
"<ul><li>Drag with {scene-select-primary} (make instances the selection) or {scene-select-secondary} "
"(invert selection state) from the centre of the selection sphere outwards</li>"

@ -156,6 +156,7 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setSelectionMode (QAbstractItemView::ExtendedSelection);
mProxyModel = new QSortFilterProxyModel (this);
mProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
mProxyModel->setSourceModel (mModel);
mProxyModel->setSortRole(Qt::UserRole);

@ -57,12 +57,12 @@ CSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id,
// left section
mPrevButton = new QToolButton (this);
mPrevButton->setIcon(QIcon(":/go-previous.png"));
mPrevButton->setIcon(QIcon(":record-previous"));
mPrevButton->setToolTip ("Switch to previous record");
buttonsLayout->addWidget (mPrevButton, 0);
mNextButton = new QToolButton (this);
mNextButton->setIcon(QIcon(":/go-next.png"));
mNextButton->setIcon(QIcon(":/record-next"));
mNextButton->setToolTip ("Switch to next record");
buttonsLayout->addWidget (mNextButton, 1);
@ -72,7 +72,7 @@ CSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id,
if (mTable.getFeatures() & CSMWorld::IdTable::Feature_Preview)
{
QToolButton* previewButton = new QToolButton (this);
previewButton->setIcon(QIcon(":/edit-preview.png"));
previewButton->setIcon(QIcon(":edit-preview"));
previewButton->setToolTip ("Open a preview of this record");
buttonsLayout->addWidget(previewButton);
connect (previewButton, SIGNAL(clicked()), this, SIGNAL (showPreview()));
@ -89,22 +89,22 @@ CSVWorld::RecordButtonBar::RecordButtonBar (const CSMWorld::UniversalId& id,
// right section
mCloneButton = new QToolButton (this);
mCloneButton->setIcon(QIcon(":/edit-clone.png"));
mCloneButton->setIcon(QIcon(":edit-clone"));
mCloneButton->setToolTip ("Clone record");
buttonsLayout->addWidget(mCloneButton);
mAddButton = new QToolButton (this);
mAddButton->setIcon(QIcon(":/add.png"));
mAddButton->setIcon(QIcon(":edit-add"));
mAddButton->setToolTip ("Add new record");
buttonsLayout->addWidget(mAddButton);
mDeleteButton = new QToolButton (this);
mDeleteButton->setIcon(QIcon(":/edit-delete.png"));
mDeleteButton->setIcon(QIcon(":edit-delete"));
mDeleteButton->setToolTip ("Delete record");
buttonsLayout->addWidget(mDeleteButton);
mRevertButton = new QToolButton (this);
mRevertButton->setIcon(QIcon(":/edit-undo.png"));
mRevertButton->setIcon(QIcon(":edit-undo"));
mRevertButton->setToolTip ("Revert record");
buttonsLayout->addWidget(mRevertButton);

@ -27,7 +27,7 @@ CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()
static const char *sIcons[] =
{
":./base.png", ":./modified.png", ":./added.png", ":./removed.png", ":./removed.png", 0
":list-base", ":list-modified", ":list-added", ":list-removed", ":list-removed", 0
};
for (int i=0; sIcons[i]; ++i)

@ -63,19 +63,9 @@ std::string CSVWorld::ReferenceCreator::getErrors() const
std::string cell = mCell->text().toUtf8().constData();
if (cell.empty())
{
if (!errors.empty())
errors += "<br>";
errors += "Missing Cell ID";
}
else if (getData().getCells().searchId (cell)==-1)
{
if (!errors.empty())
errors += "<br>";
errors += "Invalid Cell ID";
}
return errors;
}

@ -764,10 +764,8 @@ std::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const
QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<CSMWorld::UniversalId> idToDrag;
foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW.
{
for (QModelIndex& it : selectedRows)
idToDrag.push_back (getUniversalId (it.row()));
}
return idToDrag;
}

@ -89,7 +89,7 @@ add_openmw_dir (mwmechanics
)
add_openmw_dir (mwstate
statemanagerimp charactermanager character
statemanagerimp charactermanager character quicksavemanager
)
add_openmw_dir (mwbase
@ -223,4 +223,3 @@ endif (MSVC)
if (WIN32)
INSTALL(TARGETS openmw RUNTIME DESTINATION ".")
endif (WIN32)

@ -35,28 +35,11 @@ int Java_org_libsdl_app_SDLActivity_isMouseShown(JNIEnv *env, jclass cls, jobjec
return SDL_ShowCursor(SDL_QUERY);
}
int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls,
jobject obj) {
int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) {
setenv("OPENMW_DECOMPRESS_TEXTURES", "1", 1);
SDL_Android_Init(env, cls);
SDL_SetMainReady();
// On Android, we use a virtual controller with guid="Virtual"
SDL_GameControllerAddMapping("5669727475616c000000000000000000,Virtual,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4");
/* Run the application code! */
int status;
status = main(argcData+1, argvData);
releaseArgv();
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
/* exit(status); */
return status;
return 0;
}

@ -39,6 +39,7 @@ namespace MWBase
class ResponseCallback
{
public:
virtual ~ResponseCallback() = default;
virtual void addResponse(const std::string& title, const std::string& text) = 0;
};

@ -337,7 +337,7 @@ namespace MWBase
/// Cycle to next or previous weapon
virtual void cycleWeapon(bool next) = 0;
virtual void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f) = 0;
virtual void playSound(const std::string& soundId, bool preventOverlapping = false, float volume = 1.f, float pitch = 1.f) = 0;
// In WindowManager for now since there isn't a VFS singleton
virtual std::string correctIconPath(const std::string& path) = 0;

@ -491,12 +491,14 @@ namespace MWBase
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0;
virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0;
virtual const std::vector<std::string>& getContentFiles() const = 0;
virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0;
// Are we in an exterior or pseudo-exterior cell and it's night?
virtual bool isDark() const = 0;
// Allow NPCs to use torches?
virtual bool useTorches() const = 0;
virtual bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) = 0;

@ -233,24 +233,6 @@ namespace MWClass
if (!(ref->mBase->mData.mFlags & ESM::Light::Carry))
return std::make_pair(0,"");
const MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc);
MWWorld::ConstContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == invStore.end())
return std::make_pair(1,"");
/// \todo the 2h check is repeated many times; put it in a function
if(weapon->getTypeName() == typeid(ESM::Weapon).name() &&
(weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoClose ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::BluntTwoWide ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::SpearTwoWide ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::AxeTwoHand ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanBow ||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow))
{
return std::make_pair(3,"");
}
return std::make_pair(1,"");
}

@ -83,8 +83,9 @@ namespace MWClass
if (ptr.getCellRef().getSoul() != "")
{
const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(ref->mRef.getSoul());
value *= creature->mData.mSoul;
const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ref->mRef.getSoul());
if (creature)
value *= creature->mData.mSoul;
}
return value;
@ -148,8 +149,9 @@ namespace MWClass
if (ref->mRef.getSoul() != "")
{
const ESM::Creature *creature = store.get<ESM::Creature>().find(ref->mRef.getSoul());
info.caption += " (" + creature->mName + ")";
const ESM::Creature *creature = store.get<ESM::Creature>().search(ref->mRef.getSoul());
if (creature)
info.caption += " (" + creature->mName + ")";
}
std::string text;
@ -210,7 +212,7 @@ namespace MWClass
std::shared_ptr<MWWorld::Action> Miscellaneous::use (const MWWorld::Ptr& ptr) const
{
if (ptr.getCellRef().getSoul().empty())
if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ptr.getCellRef().getSoul()))
return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction());
else
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionSoulgem(ptr));

@ -312,40 +312,40 @@ namespace MWClass
int gold=0;
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
gold = ref->mBase->mNpdt52.mGold;
gold = ref->mBase->mNpdt.mGold;
for (unsigned int i=0; i< ESM::Skill::Length; ++i)
data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]);
data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt.mSkills[i]);
data->mNpcStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mNpdt52.mStrength);
data->mNpcStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mNpdt52.mIntelligence);
data->mNpcStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mNpdt52.mWillpower);
data->mNpcStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mNpdt52.mAgility);
data->mNpcStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mNpdt52.mSpeed);
data->mNpcStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mNpdt52.mEndurance);
data->mNpcStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mNpdt52.mPersonality);
data->mNpcStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mNpdt52.mLuck);
data->mNpcStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mNpdt.mStrength);
data->mNpcStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mNpdt.mIntelligence);
data->mNpcStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mNpdt.mWillpower);
data->mNpcStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mNpdt.mAgility);
data->mNpcStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mNpdt.mSpeed);
data->mNpcStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mNpdt.mEndurance);
data->mNpcStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mNpdt.mPersonality);
data->mNpcStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mNpdt.mLuck);
data->mNpcStats.setHealth (ref->mBase->mNpdt52.mHealth);
data->mNpcStats.setMagicka (ref->mBase->mNpdt52.mMana);
data->mNpcStats.setFatigue (ref->mBase->mNpdt52.mFatigue);
data->mNpcStats.setHealth (ref->mBase->mNpdt.mHealth);
data->mNpcStats.setMagicka (ref->mBase->mNpdt.mMana);
data->mNpcStats.setFatigue (ref->mBase->mNpdt.mFatigue);
data->mNpcStats.setLevel(ref->mBase->mNpdt52.mLevel);
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt52.mDisposition);
data->mNpcStats.setReputation(ref->mBase->mNpdt52.mReputation);
data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel);
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition);
data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);
data->mNpcStats.setNeedRecalcDynamicStats(false);
}
else
{
gold = ref->mBase->mNpdt12.mGold;
gold = ref->mBase->mNpdt.mGold;
for (int i=0; i<3; ++i)
data->mNpcStats.setDynamic (i, 10);
data->mNpcStats.setLevel(ref->mBase->mNpdt12.mLevel);
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition);
data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation);
data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel);
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition);
data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);
autoCalculateAttributes(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats, ptr);
@ -406,7 +406,7 @@ namespace MWClass
// store
ptr.getRefData().setCustomData (data.release());
getInventoryStore(ptr).autoEquip(ptr);
getInventoryStore(ptr).autoEquip(ptr);
}
}
@ -1327,10 +1327,7 @@ namespace MWClass
int Npc::getBaseGold(const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
return ref->mBase->mNpdt52.mGold;
else
return ref->mBase->mNpdt12.mGold;
return ref->mBase->mNpdt.mGold;
}
bool Npc::isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const

@ -892,7 +892,7 @@ protected:
public:
typedef TypesetBookImpl::StyleImpl Style;
typedef std::map <TextFormat::Id, TextFormat*> ActiveTextFormats;
typedef std::map <TextFormat::Id, std::unique_ptr<TextFormat>> ActiveTextFormats;
int mViewTop;
int mViewBottom;
@ -1048,7 +1048,7 @@ public:
{
if (mNode != NULL)
i->second->destroyDrawItem (mNode);
delete i->second;
i->second.reset();
}
mActiveTextFormats.clear ();
@ -1115,11 +1115,11 @@ public:
if (j == this_->mActiveTextFormats.end ())
{
TextFormat * textFormat = new TextFormat (Font, this_);
std::unique_ptr<TextFormat> textFormat(new TextFormat (Font, this_));
textFormat->mTexture = Font->getTextureFont ();
j = this_->mActiveTextFormats.insert (std::make_pair (Font, textFormat)).first;
j = this_->mActiveTextFormats.insert (std::make_pair (Font, std::move(textFormat))).first;
}
j->second->mCountVertex += run.mPrintableChars * 6;

@ -200,7 +200,7 @@ namespace MWGui
{
if ((mCurrentPage+1)*2 < mPages.size())
{
MWBase::Environment::get().getWindowManager()->playSound("book page2");
MWBase::Environment::get().getWindowManager()->playSound("book page2", true);
++mCurrentPage;
@ -211,7 +211,7 @@ namespace MWGui
{
if (mCurrentPage > 0)
{
MWBase::Environment::get().getWindowManager()->playSound("book page");
MWBase::Environment::get().getWindowManager()->playSound("book page", true);
--mCurrentPage;

@ -129,6 +129,12 @@ namespace MWGui
mItemView->setModel(mSortModel);
mFilterAll->setStateSelected(true);
mFilterWeapon->setStateSelected(false);
mFilterApparel->setStateSelected(false);
mFilterMagic->setStateSelected(false);
mFilterMisc->setStateSelected(false);
mPreview->updatePtr(mPtr);
mPreview->rebuild();
mPreview->update();

@ -16,8 +16,13 @@ namespace
{
if (count == 1)
return "";
if (count > 9999)
return MyGUI::utility::toString(int(count/1000.f)) + "k";
if (count > 999999999)
return MyGUI::utility::toString(count/1000000000) + "b";
else if (count > 999999)
return MyGUI::utility::toString(count/1000000) + "m";
else if (count > 9999)
return MyGUI::utility::toString(count/1000) + "k";
else
return MyGUI::utility::toString(count);
}

@ -616,7 +616,7 @@ namespace
if (page+2 < book->pageCount())
{
MWBase::Environment::get().getWindowManager()->playSound("book page");
MWBase::Environment::get().getWindowManager()->playSound("book page", true);
page += 2;
updateShowingPages ();
@ -634,7 +634,7 @@ namespace
if(page >= 2)
{
MWBase::Environment::get().getWindowManager()->playSound("book page");
MWBase::Environment::get().getWindowManager()->playSound("book page", true);
page -= 2;
updateShowingPages ();

@ -39,6 +39,7 @@ void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector<MyGUI::Widget*>& resu
KeyboardNavigation::KeyboardNavigation()
: mCurrentFocus(nullptr)
, mModalWindow(nullptr)
, mEnabled(true)
{
MyGUI::WidgetManager::getInstance().registerUnlinker(this);
}
@ -101,6 +102,9 @@ bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root)
void KeyboardNavigation::onFrame()
{
if (!mEnabled)
return;
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (focus == mCurrentFocus)
@ -150,6 +154,11 @@ void KeyboardNavigation::setModalWindow(MyGUI::Widget *window)
mModalWindow = window;
}
void KeyboardNavigation::setEnabled(bool enabled)
{
mEnabled = enabled;
}
enum Direction
{
D_Left,
@ -162,6 +171,9 @@ enum Direction
bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text)
{
if (!mEnabled)
return false;
switch (key.getValue())
{
case MyGUI::KeyCode::ArrowLeft:

@ -28,6 +28,8 @@ namespace MWGui
void setModalWindow(MyGUI::Widget* window);
void setEnabled(bool enabled);
private:
bool switchFocus(int direction, bool wrap);
@ -40,6 +42,8 @@ namespace MWGui
MyGUI::Widget* mCurrentFocus;
MyGUI::Widget* mModalWindow;
bool mEnabled;
};
}

@ -330,7 +330,12 @@ namespace MWGui
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
if (!spells.hasSpell(spellId))
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
MWBase::Environment::get().getWindowManager()->messageBox (
"#{sQuickMenu5} " + spell->mName);
return;
}
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);

@ -15,11 +15,8 @@ namespace MWGui
// check if count of the reference has become 0
if (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)
{
if (!mPtr.isEmpty())
{
mPtr = MWWorld::Ptr();
onReferenceUnavailable();
}
mPtr = MWWorld::Ptr();
onReferenceUnavailable();
}
}
}

@ -208,7 +208,7 @@ namespace MWGui
if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted))
return false;
if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name()
|| base.getCellRef().getSoul() == ""))
|| base.getCellRef().getSoul() == "" || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(base.getCellRef().getSoul())))
return false;
if ((mFilter & Filter_OnlyRepairTools) && (base.getTypeName() != typeid(ESM::Repair).name()))
return false;

@ -4,6 +4,8 @@
#include <MyGUI_ScrollView.h>
#include <MyGUI_Gui.h>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -72,7 +74,10 @@ namespace MWGui
MWWorld::ActionTeleport::getFollowersToTeleport(player, followers);
// Apply followers cost, in vanilla one follower travels for free
price *= std::max(1, static_cast<int>(followers.size()));
if (Settings::Manager::getBool("charge for every follower travelling", "Game"))
price *= 1 + static_cast<int>(followers.size());
else
price *= std::max(1, static_cast<int>(followers.size()));
MyGUI::Button* toAdd = mDestinationsView->createWidget<MyGUI::Button>("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default);
toAdd->setEnabled(price <= playerGold);

@ -533,10 +533,10 @@ namespace MWGui
}
MWScrollBar::MWScrollBar()
: mEnableRepeat(true)
, mRepeatTriggerTime(0.5f)
, mRepeatStepTime(0.1f)
, mIsIncreasing(true)
: mEnableRepeat(true)
, mRepeatTriggerTime(0.5f)
, mRepeatStepTime(0.1f)
, mIsIncreasing(true)
{
#if MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3,2,2)
ScrollBar::setRepeatEnabled(false);

@ -25,7 +25,7 @@ void WindowBase::setVisible(bool visible)
if (visible)
onOpen();
else if (wasVisible && !visible)
else if (wasVisible)
onClose();
// This is needed as invisible widgets can retain key focus.

@ -237,7 +237,10 @@ namespace MWGui
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
MyGUI::ResourceManager::getInstance().load("core.xml");
bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI");
mKeyboardNavigation.reset(new KeyboardNavigation());
mKeyboardNavigation->setEnabled(keyboardNav);
Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav);
mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer);
mWindows.push_back(mLoadingScreen);
@ -1916,11 +1919,16 @@ namespace MWGui
mInventoryWindow->cycle(next);
}
void WindowManager::playSound(const std::string& soundId, float volume, float pitch)
void WindowManager::playSound(const std::string& soundId, bool preventOverlapping, float volume, float pitch)
{
if (soundId.empty())
return;
MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);
MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager();
if (preventOverlapping && sndmgr->getSoundPlaying(MWWorld::Ptr(), soundId))
return;
sndmgr->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);
}
void WindowManager::updateSpellWindow()

@ -366,7 +366,7 @@ namespace MWGui
/// Cycle to next or previous weapon
virtual void cycleWeapon(bool next);
virtual void playSound(const std::string& soundId, float volume = 1.f, float pitch = 1.f);
virtual void playSound(const std::string& soundId, bool preventOverlapping = false, float volume = 1.f, float pitch = 1.f);
// In WindowManager for now since there isn't a VFS singleton
virtual std::string correctIconPath(const std::string& path);

@ -524,30 +524,29 @@ namespace MWInput
isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25;
if(triedToMove) resetIdleTime();
if (actionIsActive(A_MoveLeft))
if (actionIsActive(A_MoveLeft) && !actionIsActive(A_MoveRight))
{
triedToMove = true;
mPlayer->setLeftRight (-1);
}
else if (actionIsActive(A_MoveRight))
else if (actionIsActive(A_MoveRight) && !actionIsActive(A_MoveLeft))
{
triedToMove = true;
mPlayer->setLeftRight (1);
}
if (actionIsActive(A_MoveForward))
if (actionIsActive(A_MoveForward) && !actionIsActive(A_MoveBackward))
{
triedToMove = true;
mPlayer->setAutoMove (false);
mPlayer->setForwardBackward (1);
}
else if (actionIsActive(A_MoveBackward))
else if (actionIsActive(A_MoveBackward) && !actionIsActive(A_MoveForward))
{
triedToMove = true;
mPlayer->setAutoMove (false);
mPlayer->setForwardBackward (-1);
}
else if(mPlayer->getAutoMove())
{
triedToMove = true;

@ -391,7 +391,7 @@ namespace MWMechanics
{
// Player followers and escorters with high fight should not initiate combat with the player or with
// other player followers or escorters
if (std::find(playerAllies.begin(), playerAllies.end(), actor1) == playerAllies.end())
if (!isPlayerFollowerOrEscorter)
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
}
}
@ -900,7 +900,7 @@ namespace MWMechanics
stats.setTimeToStartDrowning(fHoldBreathTime);
}
void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration)
void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip)
{
bool isPlayer = (ptr == getPlayer());
@ -922,7 +922,7 @@ namespace MWMechanics
}
}
if (MWBase::Environment::get().getWorld()->isDark())
if (mayEquip)
{
if (torch != inventoryStore.end())
{
@ -931,16 +931,11 @@ namespace MWMechanics
// For non-hostile NPCs, unequip whatever is in the left slot in favor of a light.
if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name())
inventoryStore.unequipItem(*heldIter, ptr);
// Also unequip twohanded weapons which conflict with anything in CarriedLeft
if (torch->getClass().canBeEquipped(*torch, ptr).first == 3)
inventoryStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, ptr);
}
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
// If we have a torch and can equip it (left slot free, no
// twohanded weapon in right slot), then equip it now.
// If we have a torch and can equip it, then equip it now.
if (heldIter == inventoryStore.end()
&& torch->getClass().canBeEquipped(*torch, ptr).first == 1)
{
@ -997,7 +992,7 @@ namespace MWMechanics
}
}
void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration)
void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration)
{
MWWorld::Ptr player = getPlayer();
if (ptr != player && ptr.getClass().isNpc())
@ -1204,6 +1199,9 @@ namespace MWMechanics
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
// show torches only when there are darkness and no precipitations
bool showTorches = MWBase::Environment::get().getWorld()->useTorches();
MWWorld::Ptr player = getPlayer();
/// \todo move update logic to Actor class where appropriate
@ -1287,7 +1285,7 @@ namespace MWMechanics
}
if (iter->first.getClass().isNpc() && iter->first != player)
updateCrimePersuit(iter->first, duration);
updateCrimePursuit(iter->first, duration);
if (iter->first != player)
{
@ -1302,7 +1300,7 @@ namespace MWMechanics
updateNpc(iter->first, duration);
if (timerUpdateEquippedLight == 0)
updateEquippedLight(iter->first, updateEquippedLightInterval);
updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches);
}
}
}
@ -1424,7 +1422,7 @@ namespace MWMechanics
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
break;
}
else if (!detected)
else
avoidedNotice = true;
}
}

@ -39,9 +39,9 @@ namespace MWMechanics
void updateDrowning (const MWWorld::Ptr& ptr, float duration);
void updateEquippedLight (const MWWorld::Ptr& ptr, float duration);
void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip);
void updateCrimePersuit (const MWWorld::Ptr& ptr, float duration);
void updateCrimePursuit (const MWWorld::Ptr& ptr, float duration);
void killDeadActors ();

@ -189,7 +189,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
MWWorld::Ptr door = getNearbyDoor(actor, distance);
if (door != MWWorld::Ptr())
if (door != MWWorld::Ptr() && actor.getClass().isBipedal(actor))
{
// note: AiWander currently does not open doors
if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == 0)
@ -224,7 +224,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
MWBase::Environment::get().getWorld()->activate(door, actor);
}
}
else // any other obstacle (NPC, crate, etc.)
else
{
mObstacleCheck.takeEvasiveAction(movement);
}

@ -14,7 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* version 3 along with this program. If not, see
* http://www.gnu.org/licenses/ .
* https://www.gnu.org/licenses/ .
*/
#include "character.hpp"
@ -286,7 +286,7 @@ void CharacterController::refreshHitRecoilAnims()
}
else if (recovery)
{
std::string anim = isSwimming ? chooseRandomGroup("swimhit") : chooseRandomGroup("hit");
std::string anim = chooseRandomGroup("swimhit");
if (isSwimming && mAnimation->hasAnimation(anim))
{
mHitState = CharState_SwimHit;
@ -517,8 +517,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force)
{
if(force || idle != mIdleState ||
((idle == mIdleState) && !mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty()))
if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty()))
{
mIdleState = idle;
size_t numLoops = ~0ul;
@ -1189,7 +1188,7 @@ bool CharacterController::updateWeaponState()
std::string weapgroup;
if(weaptype == WeapType_None)
{
if ((!isWerewolf || mWeaponType != WeapType_Spell))
if (!isWerewolf || mWeaponType != WeapType_Spell)
{
getWeaponGroup(mWeaponType, weapgroup);
mAnimation->play(weapgroup, priorityWeapon,
@ -2152,7 +2151,7 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
MWRender::Animation::BlendMask_All, false, 1.0f,
((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1, loopfallback);
}
else if(mode == 0)
else
{
mAnimQueue.resize(1);
mAnimQueue.push_back(entry);

@ -1,6 +1,7 @@
#include "combat.hpp"
#include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
@ -155,7 +156,11 @@ namespace MWMechanics
if (!(weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver
|| weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Magical))
damage *= multiplier;
{
if (weapon.getClass().getEnchantment(weapon).empty()
|| !Settings::Manager::getBool("enchanted weapons are magical", "Game"))
damage *= multiplier;
}
if ((weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver)
&& actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())

@ -242,8 +242,11 @@ namespace MWMechanics
return 0;
if(mSoulGemPtr.getCellRef().getSoul()=="")
return 0;
const ESM::Creature* soul = store.get<ESM::Creature>().find(mSoulGemPtr.getCellRef().getSoul());
return soul->mData.mSoul;
const ESM::Creature* soul = store.get<ESM::Creature>().search(mSoulGemPtr.getCellRef().getSoul());
if(soul)
return soul->mData.mSoul;
else
return 0;
}
int Enchanting::getMaxEnchantValue() const

@ -79,21 +79,21 @@ namespace MWMechanics
const ESM::NPC *player = ptr.get<ESM::NPC>()->mBase;
// reset
creatureStats.setLevel(player->mNpdt52.mLevel);
creatureStats.setLevel(player->mNpdt.mLevel);
creatureStats.getSpells().clear();
creatureStats.modifyMagicEffects(MagicEffects());
for (int i=0; i<27; ++i)
npcStats.getSkill (i).setBase (player->mNpdt52.mSkills[i]);
creatureStats.setAttribute(ESM::Attribute::Strength, player->mNpdt52.mStrength);
creatureStats.setAttribute(ESM::Attribute::Intelligence, player->mNpdt52.mIntelligence);
creatureStats.setAttribute(ESM::Attribute::Willpower, player->mNpdt52.mWillpower);
creatureStats.setAttribute(ESM::Attribute::Agility, player->mNpdt52.mAgility);
creatureStats.setAttribute(ESM::Attribute::Speed, player->mNpdt52.mSpeed);
creatureStats.setAttribute(ESM::Attribute::Endurance, player->mNpdt52.mEndurance);
creatureStats.setAttribute(ESM::Attribute::Personality, player->mNpdt52.mPersonality);
creatureStats.setAttribute(ESM::Attribute::Luck, player->mNpdt52.mLuck);
npcStats.getSkill (i).setBase (player->mNpdt.mSkills[i]);
creatureStats.setAttribute(ESM::Attribute::Strength, player->mNpdt.mStrength);
creatureStats.setAttribute(ESM::Attribute::Intelligence, player->mNpdt.mIntelligence);
creatureStats.setAttribute(ESM::Attribute::Willpower, player->mNpdt.mWillpower);
creatureStats.setAttribute(ESM::Attribute::Agility, player->mNpdt.mAgility);
creatureStats.setAttribute(ESM::Attribute::Speed, player->mNpdt.mSpeed);
creatureStats.setAttribute(ESM::Attribute::Endurance, player->mNpdt.mEndurance);
creatureStats.setAttribute(ESM::Attribute::Personality, player->mNpdt.mPersonality);
creatureStats.setAttribute(ESM::Attribute::Luck, player->mNpdt.mLuck);
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
@ -1118,8 +1118,11 @@ namespace MWMechanics
Misc::StringUtils::lowerCaseInPlace(owner.first);
if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))
mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count;
{
const MWWorld::Ptr victimRef = MWBase::Environment::get().getWorld()->searchPtr(ownerCellRef->getOwner(), true);
if (victimRef.isEmpty() || !victimRef.getClass().getCreatureStats(victimRef).isDead())
mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count;
}
if (alarm)
commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count);
}
@ -1442,7 +1445,20 @@ namespace MWMechanics
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
// Note: accidental or collateral damage attacks are ignored.
if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue))
startCombat(target, attacker);
{
// If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player,
// he will attack the player only if we will force him (e.g. via StartCombat console command)
bool peaceful = false;
std::string script = target.getClass().getScript(target);
if (!script.empty() && target.getRefData().getLocals().hasVar(script, "onpchitme") && attacker == getPlayer())
{
int fight = std::max(0, target.getClass().getCreatureStats(target).getAiSetting(CreatureStats::AI_Fight).getModified());
peaceful = (fight == 0);
}
if (!peaceful)
startCombat(target, attacker);
}
}
return true;
@ -1719,11 +1735,11 @@ namespace MWMechanics
{
if (werewolf)
{
player->saveSkillsAttributes();
player->setWerewolfSkillsAttributes();
player->saveStats();
player->setWerewolfStats();
}
else
player->restoreSkillsAttributes();
player->restoreStats();
}
// Werewolfs can not cast spells, so we need to unset the prepared spell if there is one.

@ -214,7 +214,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_,
}
}
void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress)
void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook)
{
int base = getSkill (skillIndex).getBase();
@ -256,9 +256,14 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
MWBase::Environment::get().getWindowManager()->playSound("skillraise");
std::stringstream message;
if (readBook)
message << std::string("#{sBookSkillMessage}\n");
message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", ""))
% std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}")
% static_cast<int> (base);
MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never);
if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt())

@ -81,7 +81,7 @@ namespace MWMechanics
void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor=1.f);
///< Increase skill by usage.
void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress);
void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook = false);
int getLevelProgress() const;

@ -316,7 +316,7 @@ namespace MWMechanics
buildPath(startPoint, endPoint, cell, pathgridGraph);
if (mPath.size() >= 2)
{
// if 2nd waypoint of new path == 1st waypoint of old,
// if 2nd waypoint of new path == 1st waypoint of old,
// delete 1st waypoint of new path.
std::list<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin();
if (iter->mX == oldStart.mX

@ -8,7 +8,7 @@
namespace
{
// See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
// See https://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
//
// One of the smallest cost in Seyda Neen is between points 77 & 78:
// pt x y

@ -7,6 +7,7 @@
#include <boost/format.hpp>
#include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
@ -24,7 +25,6 @@
#include "../mwrender/animation.hpp"
#include "magiceffects.hpp"
#include "npcstats.hpp"
#include "actorutil.hpp"
#include "aifollow.hpp"
@ -489,6 +489,9 @@ namespace MWMechanics
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
}
if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
magnitudeMult = 0;
// If player is attempting to cast a harmful spell, show the target's HP bar
if (castByPlayer && target != caster)
MWBase::Environment::get().getWindowManager()->setEnemy(target);
@ -567,9 +570,12 @@ namespace MWMechanics
ActiveSpells::ActiveEffect effect_ = effect;
effect_.mMagnitude *= -1;
absorbEffects.push_back(effect_);
// Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId());
if (reflected && Settings::Manager::getBool("classic reflect absorb attribute behavior", "Game"))
target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true,
absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId());
else
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId());
}
}
}
@ -609,7 +615,6 @@ namespace MWMechanics
std::string texture = magicEffect->mParticle;
// TODO: VFX are no longer active after saving/reloading the game
bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
// Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
@ -877,20 +882,18 @@ namespace MWMechanics
const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);
float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue);
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
stats.setFatigue(fatigue);
bool fail = false;
// Check success
if (!(mCaster == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState()))
float successChance = getSpellSuccessChance(spell, mCaster);
if (Misc::Rng::roll0to99() >= successChance)
{
float successChance = getSpellSuccessChance(spell, mCaster);
if (Misc::Rng::roll0to99() >= successChance)
{
if (mCaster == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
fail = true;
}
if (mCaster == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
fail = true;
}
if (fail)
@ -1111,8 +1114,6 @@ namespace MWMechanics
bool receivedMagicDamage = false;
bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
switch (effectKey.mId)
{
case ESM::MagicEffect::DamageAttribute:
@ -1135,40 +1136,25 @@ namespace MWMechanics
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude);
break;
case ESM::MagicEffect::DamageHealth:
if (!godmode)
{
receivedMagicDamage = true;
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
}
receivedMagicDamage = true;
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
break;
case ESM::MagicEffect::DamageMagicka:
case ESM::MagicEffect::DamageFatigue:
if (!godmode)
{
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
}
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
break;
case ESM::MagicEffect::AbsorbHealth:
if (!godmode)
{
if (magnitude > 0.f)
receivedMagicDamage = true;
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
}
if (magnitude > 0.f)
receivedMagicDamage = true;
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
break;
case ESM::MagicEffect::AbsorbMagicka:
case ESM::MagicEffect::AbsorbFatigue:
if (!godmode)
{
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
}
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
break;
case ESM::MagicEffect::DisintegrateArmor:
@ -1191,6 +1177,7 @@ namespace MWMechanics
if (disintegrateSlot(actor, priorities[i], magnitude))
break;
}
break;
}
case ESM::MagicEffect::DisintegrateWeapon:
@ -1213,12 +1200,9 @@ namespace MWMechanics
if (weather > 1)
damageScale *= fMagicSunBlockedMult;
if (!godmode)
{
adjustDynamicStat(creatureStats, 0, -magnitude * damageScale);
if (magnitude * damageScale > 0.f)
receivedMagicDamage = true;
}
adjustDynamicStat(creatureStats, 0, -magnitude * damageScale);
if (magnitude * damageScale > 0.f)
receivedMagicDamage = true;
break;
}
@ -1228,12 +1212,8 @@ namespace MWMechanics
case ESM::MagicEffect::FrostDamage:
case ESM::MagicEffect::Poison:
{
if (!godmode)
{
adjustDynamicStat(creatureStats, 0, -magnitude);
receivedMagicDamage = true;
}
adjustDynamicStat(creatureStats, 0, -magnitude);
receivedMagicDamage = true;
break;
}
@ -1314,4 +1294,24 @@ namespace MWMechanics
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(it->second)->getString();
}
void ApplyLoopingParticlesVisitor::visit (MWMechanics::EffectKey key,
const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,
float /*magnitude*/, float /*remainingTime*/, float /*totalTime*/)
{
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(key.mId);
const ESM::Static* castStatic;
if (!magicEffect->mHit.empty())
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
else
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
std::string texture = magicEffect->mParticle;
bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mActor);
if (anim && loop)
anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", texture);
}
}

@ -6,6 +6,8 @@
#include "../mwworld/ptr.hpp"
#include "magiceffects.hpp"
namespace ESM
{
struct Spell;
@ -119,6 +121,21 @@ namespace MWMechanics
bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude);
};
class ApplyLoopingParticlesVisitor : public EffectSourceVisitor
{
private:
MWWorld::Ptr mActor;
public:
ApplyLoopingParticlesVisitor(const MWWorld::Ptr& actor)
: mActor(actor)
{
}
virtual void visit (MWMechanics::EffectKey key,
const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,
float /*magnitude*/, float /*remainingTime*/ = -1, float /*totalTime*/ = -1);
};
}
#endif

@ -13,7 +13,6 @@
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <LinearMath/btQuickprof.h>

@ -1,5 +1,6 @@
#include "characterpreview.hpp"
#include <cmath>
#include <iostream>
#include <osg/Material>
@ -13,6 +14,7 @@
#include <osgUtil/IntersectionVisitor>
#include <osgUtil/LineSegmentIntersector>
#include <components/fallback/fallback.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/shadow.hpp>
@ -161,14 +163,25 @@ namespace MWRender
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
lightmodel->setAmbientIntensity(osg::Vec4(0.25, 0.25, 0.25, 1.0));
lightmodel->setAmbientIntensity(osg::Vec4(0.0, 0.0, 0.0, 1.0));
stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON);
/// \todo Read the fallback values from INIImporter (Inventory:Directional*) ?
osg::ref_ptr<osg::Light> light = new osg::Light;
light->setPosition(osg::Vec4(-0.3,0.3,0.7, 0.0));
light->setDiffuse(osg::Vec4(1,1,1,1));
light->setAmbient(osg::Vec4(0,0,0,1));
const Fallback::Map* fallback = MWBase::Environment::get().getWorld()->getFallback();
float diffuseR = fallback->getFallbackFloat("Inventory_DirectionalDiffuseR");
float diffuseG = fallback->getFallbackFloat("Inventory_DirectionalDiffuseG");
float diffuseB = fallback->getFallbackFloat("Inventory_DirectionalDiffuseB");
float ambientR = fallback->getFallbackFloat("Inventory_DirectionalAmbientR");
float ambientG = fallback->getFallbackFloat("Inventory_DirectionalAmbientG");
float ambientB = fallback->getFallbackFloat("Inventory_DirectionalAmbientB");
float azimuth = osg::DegreesToRadians(180.f - fallback->getFallbackFloat("Inventory_DirectionalRotationX"));
float altitude = osg::DegreesToRadians(fallback->getFallbackFloat("Inventory_DirectionalRotationY"));
float positionX = std::cos(azimuth) * std::sin(altitude);
float positionY = std::sin(azimuth) * std::sin(altitude);
float positionZ = std::cos(altitude);
light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0));
light->setDiffuse(osg::Vec4(diffuseR,diffuseG,diffuseB,1));
light->setAmbient(osg::Vec4(ambientR,ambientG,ambientB,1));
light->setSpecular(osg::Vec4(0,0,0,0));
light->setLightNum(0);
light->setConstantAttenuation(1.f);

@ -8,6 +8,7 @@
#include <osg/Texture2D>
#include <osg/ComputeBoundsVisitor>
#include <osg/LightSource>
#include <osg/PolygonMode>
#include <osgDB/ReadFile>
@ -175,6 +176,7 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
camera->setNodeMask(Mask_RenderToTexture);
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE);
// assign large value to effectively turn off fog
// shaders don't respect glDisable(GL_FOG)

@ -52,6 +52,16 @@
#include "terrainstorage.hpp"
#include "util.hpp"
namespace
{
float DLLandFogStart;
float DLLandFogEnd;
float DLUnderwaterFogStart;
float DLUnderwaterFogEnd;
float DLInteriorFogStart;
float DLInteriorFogEnd;
}
namespace MWRender
{
@ -174,14 +184,18 @@ namespace MWRender
, mResourceSystem(resourceSystem)
, mWorkQueue(workQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue)
, mFogDepth(0.f)
, mLandFogStart(0.f)
, mLandFogEnd(std::numeric_limits<float>::max())
, mUnderwaterFogStart(0.f)
, mUnderwaterFogEnd(std::numeric_limits<float>::max())
, mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor"))
, mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight"))
, mUnderwaterFog(0.f)
, mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog"))
, mNightEyeFactor(0.f)
, mFieldOfViewOverride(0.f)
, mDistantFog(false)
, mDistantTerrain(false)
, mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f)
{
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
@ -239,12 +253,20 @@ namespace MWRender
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath));
const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog");
DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog");
DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog");
DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog");
DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog");
DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog");
mDistantFog = Settings::Manager::getBool("use distant fog", "Fog");
mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"),
Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"),
Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"),
Settings::Manager::getBool("auto use terrain specular maps", "Shaders"));
if (distantTerrain)
if (mDistantTerrain)
mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile));
else
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile));
@ -506,14 +528,44 @@ namespace MWRender
{
osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog);
configureFog (cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, color);
if(mDistantFog)
{
float density = std::max(0.2f, cell->mAmbi.mFogDensity);
mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density);
mLandFogEnd = DLInteriorFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
mFogColor = color;
}
else
configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);
}
void RenderingManager::configureFog(float fogDepth, float underwaterFog, const osg::Vec4f &color)
void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
{
mFogDepth = fogDepth;
if(mDistantFog)
{
mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd);
mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
}
else
{
if(fogDepth == 0.0)
{
mLandFogStart = 0.0f;
mLandFogEnd = std::numeric_limits<float>::max();
}
else
{
mLandFogStart = mViewDistance * (1 - fogDepth);
mLandFogEnd = mViewDistance;
}
mUnderwaterFogStart = mViewDistance * (1 - underwaterFog);
mUnderwaterFogEnd = mViewDistance;
}
mFogColor = color;
mUnderwaterFog = underwaterFog;
}
SkyManager* RenderingManager::getSkyManager()
@ -544,23 +596,15 @@ namespace MWRender
float viewDistance = mViewDistance;
viewDistance = std::min(viewDistance, 6666.f);
setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight));
mStateUpdater->setFogStart(viewDistance * (1 - mUnderwaterFog));
mStateUpdater->setFogEnd(viewDistance);
mStateUpdater->setFogStart(mUnderwaterFogStart);
mStateUpdater->setFogEnd(mUnderwaterFogEnd);
}
else
{
setFogColor(mFogColor);
if (mFogDepth == 0.f)
{
mStateUpdater->setFogStart(0.f);
mStateUpdater->setFogEnd(std::numeric_limits<float>::max());
}
else
{
mStateUpdater->setFogStart(mViewDistance * (1 - mFogDepth));
mStateUpdater->setFogEnd(mViewDistance);
}
mStateUpdater->setFogStart(mLandFogStart);
mStateUpdater->setFogEnd(mLandFogEnd);
}
}
@ -987,7 +1031,8 @@ namespace MWRender
else if (it->first == "Camera" && it->second == "viewing distance")
{
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
mStateUpdater->setFogEnd(mViewDistance);
if(!mDistantFog)
mStateUpdater->setFogEnd(mViewDistance);
updateProjectionMatrix();
}
else if (it->first == "General" && (it->second == "texture filter" ||

@ -107,7 +107,7 @@ namespace MWRender
void configureAmbient(const ESM::Cell* cell);
void configureFog(const ESM::Cell* cell);
void configureFog(float fogDepth, float underwaterFog, const osg::Vec4f& colour);
void configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f& colour);
void addCell(const MWWorld::CellStore* store);
void removeCell(const MWWorld::CellStore* store);
@ -243,10 +243,12 @@ namespace MWRender
osg::ref_ptr<StateUpdater> mStateUpdater;
float mFogDepth;
float mLandFogStart;
float mLandFogEnd;
float mUnderwaterFogStart;
float mUnderwaterFogEnd;
osg::Vec4f mUnderwaterColor;
float mUnderwaterWeight;
float mUnderwaterFog;
float mUnderwaterIndoorFog;
osg::Vec4f mFogColor;
@ -255,8 +257,10 @@ namespace MWRender
float mNearClip;
float mViewDistance;
bool mDistantFog : 1;
bool mDistantTerrain : 1;
bool mFieldOfViewOverridden : 1;
float mFieldOfViewOverride;
bool mFieldOfViewOverridden;
float mFieldOfView;
float mFirstPersonFieldOfView;

@ -65,6 +65,9 @@ namespace MWRender
float mFogDepth;
float mDLFogFactor;
float mDLFogOffset;
float mWindSpeed;
float mCloudSpeed;

@ -503,13 +503,11 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
// use a shader to render the simple water, ensuring that fog is applied per pixel as required.
// this could be removed if a more detailed water mesh, using some sort of paging solution, is implemented.
#if !defined(OPENGL_ES) && !defined(ANDROID)
Resource::SceneManager* sceneManager = mResourceSystem->getSceneManager();
bool oldValue = sceneManager->getForceShaders();
sceneManager->setForceShaders(true);
sceneManager->recreateShaders(node);
sceneManager->setForceShaders(oldValue);
#endif
}
void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction)

@ -406,7 +406,7 @@ namespace MWScript
const MWWorld::ESMStore &store = world->getStore();
const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);
if(rank < 0 || rank > 9)
if(rank < 0)
return "";
return faction->mRanks[rank];

@ -454,7 +454,13 @@ namespace MWScript
store.get<ESM::Creature>().find(creature); // This line throws an exception if it can't find the creature
MWWorld::Ptr item = *ptr.getClass().getContainerStore(ptr).add(gem, 1, ptr);
// Set the soul on just one of the gems, not the whole stack
item.getContainerStore()->unstack(item, ptr);
item.getCellRef().setSoul(creature);
// Restack the gem with other gems with the same soul
item.getContainerStore()->restack(item);
}
};

@ -530,7 +530,8 @@ namespace MWScript
// create item
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, 1);
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), actor, actor.getCell(), direction, distance);
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), actor, actor.getCell(), direction, distance);
MWBase::Environment::get().getWorld()->scaleObject(ptr, actor.getCellRef().getScale());
}
}
};

@ -15,7 +15,7 @@
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
* Or go to https://www.gnu.org/copyleft/lgpl.html
*/
#ifndef AL_ALEXT_H

@ -21,7 +21,7 @@ extern "C"
// From version 54.56 binkaudio encoding format changed from S16 to FLTP. See:
// https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d
// http://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872
// https://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872
#include <libswresample/swresample.h>
}

@ -393,32 +393,8 @@ namespace MWSound
void SoundManager::startRandomTitle()
{
std::vector<std::string> filelist;
const std::vector<std::string> &filelist = mMusicFiles[mCurrentPlaylist];
auto &tracklist = mMusicToPlay[mCurrentPlaylist];
if (mMusicFiles.find(mCurrentPlaylist) == mMusicFiles.end())
{
const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
std::string pattern = "Music/" + mCurrentPlaylist;
mVFS->normalizeFilename(pattern);
std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(pattern);
while (found != index.end())
{
if (found->first.size() >= pattern.size() && found->first.substr(0, pattern.size()) == pattern)
filelist.push_back(found->first);
else
break;
++found;
}
mMusicFiles[mCurrentPlaylist] = filelist;
}
else
filelist = mMusicFiles[mCurrentPlaylist];
if(filelist.empty())
return;
// Do a Fisher-Yates shuffle
@ -454,6 +430,33 @@ namespace MWSound
void SoundManager::playPlaylist(const std::string &playlist)
{
if (mCurrentPlaylist == playlist)
return;
if (mMusicFiles.find(playlist) == mMusicFiles.end())
{
std::vector<std::string> filelist;
const std::map<std::string, VFS::File*>& index = mVFS->getIndex();
std::string pattern = "Music/" + playlist;
mVFS->normalizeFilename(pattern);
std::map<std::string, VFS::File*>::const_iterator found = index.lower_bound(pattern);
while (found != index.end())
{
if (found->first.size() >= pattern.size() && found->first.substr(0, pattern.size()) == pattern)
filelist.push_back(found->first);
else
break;
++found;
}
mMusicFiles[playlist] = filelist;
}
if (mMusicFiles[playlist].empty())
return;
mCurrentPlaylist = playlist;
startRandomTitle();
}

@ -0,0 +1,38 @@
#include "quicksavemanager.hpp"
MWState::QuickSaveManager::QuickSaveManager(std::string &saveName, unsigned int maxSaves)
: mSaveName(saveName)
, mMaxSaves(maxSaves)
, mSlotsVisited(0)
, mOldestSlotVisited(nullptr)
{
}
void MWState::QuickSaveManager::visitSave(const Slot *saveSlot)
{
if(mSaveName == saveSlot->mProfile.mDescription)
{
++mSlotsVisited;
if(isOldestSave(saveSlot))
mOldestSlotVisited = saveSlot;
}
}
bool MWState::QuickSaveManager::isOldestSave(const Slot *compare)
{
if(mOldestSlotVisited == NULL)
return true;
return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp);
}
bool MWState::QuickSaveManager::shouldCreateNewSlot()
{
return (mSlotsVisited < mMaxSaves);
}
const MWState::Slot *MWState::QuickSaveManager::getNextQuickSaveSlot()
{
if(shouldCreateNewSlot())
return NULL;
return mOldestSlotVisited;
}

@ -0,0 +1,35 @@
#ifndef GAME_STATE_QUICKSAVEMANAGER_H
#define GAME_STATE_QUICKSAVEMANAGER_H
#include <string>
#include "character.hpp"
#include "../mwbase/statemanager.hpp"
namespace MWState{
class QuickSaveManager{
std::string mSaveName;
unsigned int mMaxSaves;
unsigned int mSlotsVisited;
const Slot *mOldestSlotVisited;
private:
bool shouldCreateNewSlot();
bool isOldestSave(const Slot *compare);
public:
QuickSaveManager(std::string &saveName, unsigned int maxSaves);
///< A utility class to manage multiple quicksave slots
///
/// \param saveName The name of the save ("QuickSave", "AutoSave", etc)
/// \param maxSaves The maximum number of save slots to create before recycling old ones
void visitSave(const Slot *saveSlot);
///< Visits the given \a slot \a
const Slot *getNextQuickSaveSlot();
///< Get the slot that the next quicksave should use.
///
///\return Either the oldest quicksave slot visited, or NULL if a new slot can be made
};
}
#endif

@ -37,6 +37,8 @@
#include "../mwscript/globalscripts.hpp"
#include "quicksavemanager.hpp"
void MWState::StateManager::cleanup (bool force)
{
if (mState!=State_NoGame || force)
@ -324,20 +326,25 @@ void MWState::StateManager::quickSave (std::string name)
return;
}
const Slot* slot = NULL;
int maxSaves = Settings::Manager::getInt("max quicksaves", "Saves");
if(maxSaves < 1)
maxSaves = 1;
Character* currentCharacter = getCurrentCharacter(); //Get current character
QuickSaveManager saveFinder = QuickSaveManager(name, maxSaves);
//Find quicksave slot
if (currentCharacter)
{
for (Character::SlotIterator it = currentCharacter->begin(); it != currentCharacter->end(); ++it)
{
if (it->mProfile.mDescription == name)
slot = &*it;
//Visiting slots allows the quicksave finder to find the oldest quicksave
saveFinder.visitSave(&*it);
}
}
saveGame(name, slot);
//Once all the saves have been visited, the save finder can tell us which
//one to replace (or create)
saveGame(name, saveFinder.getNextQuickSaveSlot());
}
void MWState::StateManager::loadGame(const std::string& filepath)

@ -52,7 +52,7 @@ namespace MWWorld
playerRef->mBase->mClass
);
npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true);
npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true, true);
npcStats.flagAsUsed (ref->mBase->mId);
}

@ -677,7 +677,6 @@ namespace MWWorld
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)
mWaterLevel = state.mWaterLevel;
mWaterLevel = state.mWaterLevel;
mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn);
}
@ -688,7 +687,6 @@ namespace MWWorld
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)
state.mWaterLevel = mWaterLevel;
state.mWaterLevel = mWaterLevel;
state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
state.mLastRespawn = mLastRespawn.toEsm();
}

@ -256,11 +256,7 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr)
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count);
// a bit pointless to set owner for the player
if (actorPtr != MWMechanics::getPlayer())
return add(ref.getPtr(), count, actorPtr, true);
else
return add(ref.getPtr(), count, actorPtr, false);
return add(ref.getPtr(), count, actorPtr, true);
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner)

@ -141,6 +141,7 @@ void ESMStore::setUp()
mMagicEffects.setUp();
mAttributes.setUp();
mDialogs.setUp();
mStatics.setUp();
}
int ESMStore::countSavedGameRecords() const

@ -48,6 +48,12 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state)
}
mClass->readAdditionalState (ptr, state);
if (!mRef.getSoul().empty() && !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(mRef.getSoul()))
{
std::cerr << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem" << std::endl;
mRef.setSoul(std::string());
}
}
void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const

@ -47,37 +47,45 @@ namespace MWWorld
mPlayer.mData.setPosition(playerPos);
}
void Player::saveSkillsAttributes()
void Player::saveStats()
{
MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer());
for (int i=0; i<ESM::Skill::Length; ++i)
mSaveSkills[i] = stats.getSkill(i);
for (int i=0; i<ESM::Attribute::Length; ++i)
mSaveAttributes[i] = stats.getAttribute(i);
}
void Player::restoreSkillsAttributes()
void Player::restoreStats()
{
MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer());
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());
MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());
MWMechanics::DynamicStat<float> health = creatureStats.getDynamic(0);
creatureStats.setHealth(int(health.getBase() / gmst.find("fWereWolfHealth")->getFloat()));
for (int i=0; i<ESM::Skill::Length; ++i)
stats.setSkill(i, mSaveSkills[i]);
npcStats.setSkill(i, mSaveSkills[i]);
for (int i=0; i<ESM::Attribute::Length; ++i)
stats.setAttribute(i, mSaveAttributes[i]);
npcStats.setAttribute(i, mSaveAttributes[i]);
}
void Player::setWerewolfSkillsAttributes()
void Player::setWerewolfStats()
{
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer());
MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());
MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());
MWMechanics::DynamicStat<float> health = creatureStats.getDynamic(0);
creatureStats.setHealth(int(health.getBase() * gmst.find("fWereWolfHealth")->getFloat()));
for(size_t i = 0;i < ESM::Attribute::Length;++i)
{
// Oh, Bethesda. It's "Intelligence".
std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") :
ESM::Attribute::sAttributeNames[i]);
MWMechanics::AttributeValue value = stats.getAttribute(i);
MWMechanics::AttributeValue value = npcStats.getAttribute(i);
value.setBase(int(gmst.find(name)->getFloat()));
stats.setAttribute(i, value);
npcStats.setAttribute(i, value);
}
for(size_t i = 0;i < ESM::Skill::Length;i++)
@ -90,9 +98,9 @@ namespace MWWorld
std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") :
ESM::Skill::sSkillNames[i]);
MWMechanics::SkillValue value = stats.getSkill(i);
MWMechanics::SkillValue value = npcStats.getSkill(i);
value.setBase(int(gmst.find(name)->getFloat()));
stats.setSkill(i, value);
npcStats.setSkill(i, value);
}
}
@ -366,8 +374,8 @@ namespace MWWorld
if (player.mObject.mNpcStats.mWerewolfDeprecatedData && player.mObject.mNpcStats.mIsWerewolf)
{
saveSkillsAttributes();
setWerewolfSkillsAttributes();
saveStats();
setWerewolfStats();
}
getPlayer().getClass().getCreatureStats(getPlayer()).getAiSequence().clear();

@ -46,7 +46,7 @@ namespace MWWorld
int mCurrentCrimeId; // the id assigned witnesses
int mPaidCrimeId; // the last id paid off (0 bounty)
// Saved skills and attributes prior to becoming a werewolf
// Saved stats prior to becoming a werewolf
MWMechanics::SkillValue mSaveSkills[ESM::Skill::Length];
MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length];
@ -56,9 +56,9 @@ namespace MWWorld
Player(const ESM::NPC *player);
void saveSkillsAttributes();
void restoreSkillsAttributes();
void setWerewolfSkillsAttributes();
void saveStats();
void restoreStats();
void setWerewolfStats();
// For mark/recall magic effects
void markPosition (CellStore* markedCell, const ESM::Position& markedPosition);

@ -79,6 +79,9 @@ namespace
if (ptr.getClass().isActor())
rendering.addWaterRippleEmitter(ptr);
// Restore effect particles
MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr);
}
void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
@ -457,8 +460,7 @@ namespace MWWorld
float z = pos.rot[2];
world->rotateObject(player, x, y, z);
if (adjustPlayerPos)
player.getClass().adjustPosition(player, true);
player.getClass().adjustPosition(player, true);
}
MWBase::MechanicsManager *mechMgr =

@ -592,7 +592,7 @@ namespace MWWorld
const ESM::Cell *ptr = search(id);
if (ptr == 0) {
std::ostringstream msg;
msg << "Interior cell '" << id << "' not found";
msg << "Cell '" << id << "' not found";
throw std::runtime_error(msg.str());
}
return ptr;
@ -1053,6 +1053,32 @@ namespace MWWorld
}
}
template<>
void Store<ESM::Static>::setUp()
{
// Load default marker definitions, if game files do not have them for some reason
std::pair<std::string, std::string> markers[] = {
std::make_pair("divinemarker", "marker_divine.nif"),
std::make_pair("doormarker", "marker_arrow.nif"),
std::make_pair("northmarker", "marker_north.nif"),
std::make_pair("templemarker", "marker_temple.nif"),
std::make_pair("travelmarker", "marker_travel.nif")
};
for (const std::pair<std::string, std::string> marker : markers)
{
if (search(marker.first) == 0)
{
ESM::Static newMarker = ESM::Static(marker.first, marker.second);
std::pair<typename Static::iterator, bool> ret = mStatic.insert(std::make_pair(marker.first, newMarker));
if (ret.first != mStatic.end())
{
mShared.push_back(&ret.first->second);
}
}
}
}
template <>
inline RecordId Store<ESM::Dialogue>::load(ESM::ESMReader &esm) {
// The original letter case of a dialogue ID is saved, because it's printed

@ -9,7 +9,6 @@
#include <components/fallback/fallback.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/actorutil.hpp"
@ -103,6 +102,8 @@ Weather::Weather(const std::string& name,
const Fallback::Map& fallback,
float stormWindSpeed,
float rainSpeed,
float dlFactor,
float dlOffset,
const std::string& particleEffect)
: mCloudTexture(fallback.getFallbackString("Weather_" + name + "_Cloud_Texture"))
, mSkyColor(fallback.getFallbackColour("Weather_" + name +"_Sky_Sunrise_Color"),
@ -142,6 +143,8 @@ Weather::Weather(const std::string& name,
, mFlashDecrement(fallback.getFallbackFloat("Weather_" + name + "_Flash_Decrement"))
, mFlashBrightness(0.0f)
{
mDL.FogFactor = dlFactor;
mDL.FogOffset = dlOffset;
mThunderSoundID[0] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_0");
mThunderSoundID[1] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_1");
mThunderSoundID[2] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_2");
@ -520,6 +523,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall
, mSecunda("Secunda", fallback)
, mWindSpeed(0.f)
, mIsStorm(false)
, mPrecipitation(false)
, mStormDirection(0,1,0)
, mCurrentRegion()
, mTimePassed(0)
@ -541,16 +545,18 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall
mTimeSettings.mSunriseTime = mSunriseTime;
mWeatherSettings.reserve(10);
addWeather("Clear", fallback); // 0
addWeather("Cloudy", fallback); // 1
addWeather("Foggy", fallback); // 2
addWeather("Overcast", fallback); // 3
addWeather("Rain", fallback); // 4
addWeather("Thunderstorm", fallback); // 5
addWeather("Ashstorm", fallback, "meshes\\ashcloud.nif"); // 6
addWeather("Blight", fallback, "meshes\\blightcloud.nif"); // 7
addWeather("Snow", fallback, "meshes\\snow.nif"); // 8
addWeather("Blizzard", fallback, "meshes\\blizzard.nif"); // 9
// These distant land fog factor and offset values are the defaults MGE XE provides. Should be
// provided by settings somewhere?
addWeather("Clear", fallback, 1.0f, 0.0f); // 0
addWeather("Cloudy", fallback, 0.9f, 0.0f); // 1
addWeather("Foggy", fallback, 0.2f, 30.0f); // 2
addWeather("Overcast", fallback, 0.7f, 0.0f); // 3
addWeather("Rain", fallback, 0.5f, 10.0f); // 4
addWeather("Thunderstorm", fallback, 0.5f, 20.0f); // 5
addWeather("Ashstorm", fallback, 0.2f, 50.0f, "meshes\\ashcloud.nif"); // 6
addWeather("Blight", fallback, 0.2f, 60.0f, "meshes\\blightcloud.nif"); // 7
addWeather("Snow", fallback, 0.5f, 40.0f, "meshes\\snow.nif"); // 8
addWeather("Blizzard", fallback, 0.16f, 70.0f, "meshes\\blizzard.nif"); // 9
Store<ESM::Region>::iterator it = store.get<ESM::Region>().begin();
for(; it != store.get<ESM::Region>().end(); ++it)
@ -608,14 +614,11 @@ void WeatherManager::modRegion(const std::string& regionID, const std::vector<ch
}
}
void WeatherManager::playerTeleported()
void WeatherManager::playerTeleported(const std::string& playerRegion, bool isExterior)
{
// If the player teleports to an outdoors cell in a new region (for instance, by travelling), the weather needs to
// be changed immediately, and any transitions for the previous region discarded.
MWBase::World* world = MWBase::Environment::get().getWorld();
if(world->isCellExterior() || world->isCellQuasiExterior())
{
std::string playerRegion = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->getCell()->mRegion);
std::map<std::string, RegionWeather>::iterator it = mRegions.find(playerRegion);
if(it != mRegions.end() && playerRegion != mCurrentRegion)
{
@ -625,11 +628,9 @@ void WeatherManager::playerTeleported()
}
}
void WeatherManager::update(float duration, bool paused)
void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)
{
MWWorld::ConstPtr player = MWMechanics::getPlayer();
MWBase::World& world = *MWBase::Environment::get().getWorld();
TimeStamp time = world.getTimeStamp();
if(!paused || mFastForward)
{
@ -647,8 +648,7 @@ void WeatherManager::update(float duration, bool paused)
updateWeatherTransitions(duration);
}
const bool exterior = (world.isCellExterior() || world.isCellQuasiExterior());
if(!exterior)
if(!isExterior)
{
mRendering.setSkyEnabled(false);
stopSounds();
@ -660,6 +660,10 @@ void WeatherManager::update(float duration, bool paused)
mWindSpeed = mResult.mWindSpeed;
mIsStorm = mResult.mIsStorm;
// For some reason Ash Storm is not considered as a precipitation weather in game
mPrecipitation = !(mResult.mParticleEffect.empty() && mResult.mRainEffect.empty())
&& mResult.mParticleEffect != "meshes\\ashcloud.nif";
if (mIsStorm)
{
osg::Vec3f playerPos (player.getRefData().getPosition().asVec3());
@ -720,7 +724,8 @@ void WeatherManager::update(float duration, bool paused)
mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time));
mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time));
mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mFogColor);
mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor,
mResult.mDLFogOffset/100.0f, mResult.mFogColor);
mRendering.setAmbientColour(mResult.mAmbientColor);
mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView);
@ -777,12 +782,11 @@ unsigned int WeatherManager::getWeatherID() const
return mCurrentWeather;
}
bool WeatherManager::isDark() const
bool WeatherManager::useTorches(float hour) const
{
TimeStamp time = MWBase::Environment::get().getWorld()->getTimeStamp();
bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior()
|| MWBase::Environment::get().getWorld()->isCellQuasiExterior());
return exterior && (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart - 1);
bool isDark = hour < mSunriseTime || hour > mTimeSettings.mNightStart - 1;
return isDark && !mPrecipitation;
}
void WeatherManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)
@ -866,11 +870,12 @@ void WeatherManager::clear()
inline void WeatherManager::addWeather(const std::string& name,
const Fallback::Map& fallback,
float dlFactor, float dlOffset,
const std::string& particleEffect)
{
static const float fStromWindSpeed = mStore.get<ESM::GameSetting>().find("fStromWindSpeed")->getFloat();
Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, particleEffect);
Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect);
mWeatherSettings.push_back(weather);
}
@ -1058,6 +1063,8 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
mResult.mNight = (gameHour < mSunriseTime || gameHour > mTimeSettings.mNightStart - 1);
mResult.mFogDepth = current.mLandFogDepth.getValue(gameHour, mTimeSettings);
mResult.mDLFogFactor = current.mDL.FogFactor;
mResult.mDLFogOffset = current.mDL.FogOffset;
mResult.mFogColor = current.mFogColor.getValue(gameHour, mTimeSettings);
mResult.mAmbientColor = current.mAmbientColor.getValue(gameHour, mTimeSettings);
mResult.mSunColor = current.mSunColor.getValue(gameHour, mTimeSettings);
@ -1113,6 +1120,8 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
mResult.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor);
mResult.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor);
mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor);
mResult.mDLFogFactor = lerp(current.mDLFogFactor, other.mDLFogFactor, factor);
mResult.mDLFogOffset = lerp(current.mDLFogOffset, other.mDLFogOffset, factor);
mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor);
mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor);
mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor);
@ -1125,7 +1134,6 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
mResult.mIsStorm = current.mIsStorm;
mResult.mParticleEffect = current.mParticleEffect;
mResult.mRainEffect = current.mRainEffect;
mResult.mParticleEffect = current.mParticleEffect;
mResult.mRainSpeed = current.mRainSpeed;
mResult.mRainFrequency = current.mRainFrequency;
mResult.mAmbientSoundVolume = 1-(factor*2);
@ -1137,7 +1145,6 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
mResult.mIsStorm = other.mIsStorm;
mResult.mParticleEffect = other.mParticleEffect;
mResult.mRainEffect = other.mRainEffect;
mResult.mParticleEffect = other.mParticleEffect;
mResult.mRainSpeed = other.mRainSpeed;
mResult.mRainFrequency = other.mRainFrequency;
mResult.mAmbientSoundVolume = 2*(factor-0.5f);

@ -73,6 +73,8 @@ namespace MWWorld
const Fallback::Map& fallback,
float stormWindSpeed,
float rainSpeed,
float dlFactor,
float dlOffset,
const std::string& particleEffect);
std::string mCloudTexture;
@ -102,6 +104,12 @@ namespace MWWorld
// Also appears to modify how visible the sun, moons, and stars are for various weather effects.
float mGlareView;
// Fog factor and offset used with distant land rendering.
struct {
float FogFactor;
float FogOffset;
} mDL;
// Sound effect
// This is used for Blight, Ashstorm and Blizzard (Bloodmoon)
std::string mAmbientLoopSoundID;
@ -218,14 +226,14 @@ namespace MWWorld
*/
void changeWeather(const std::string& regionID, const unsigned int weatherID);
void modRegion(const std::string& regionID, const std::vector<char>& chances);
void playerTeleported();
void playerTeleported(const std::string& playerRegion, bool isExterior);
/**
* Per-frame update
* @param duration
* @param paused
*/
void update(float duration, bool paused = false);
void update(float duration, bool paused, const TimeStamp& time, bool isExterior);
void stopSounds();
@ -240,8 +248,7 @@ namespace MWWorld
unsigned int getWeatherID() const;
/// @see World::isDark
bool isDark() const;
bool useTorches(float hour) const;
void write(ESM::ESMWriter& writer, Loading::Listener& progress);
@ -275,6 +282,7 @@ namespace MWWorld
float mWindSpeed;
bool mIsStorm;
bool mPrecipitation;
osg::Vec3f mStormDirection;
std::string mCurrentRegion;
@ -293,6 +301,7 @@ namespace MWWorld
void addWeather(const std::string& name,
const Fallback::Map& fallback,
float dlFactor, float dlOffset,
const std::string& particleEffect = "");
void importRegions();

@ -406,7 +406,6 @@ namespace MWWorld
gmst["sCompanionWarningMessage"] = ESM::Variant("Warning message");
gmst["sCompanionWarningButtonOne"] = ESM::Variant("Button 1");
gmst["sCompanionWarningButtonTwo"] = ESM::Variant("Button 2");
gmst["sCompanionShare"] = ESM::Variant("Companion Share");
gmst["sProfitValue"] = ESM::Variant("Profit Value");
gmst["sTeleportDisabled"] = ESM::Variant("Teleport disabled");
gmst["sLevitateDisabled"] = ESM::Variant("Levitate disabled");
@ -2244,6 +2243,8 @@ namespace MWWorld
model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS());
mPhysics->remove(getPlayerPtr());
mPhysics->addActor(getPlayerPtr(), model);
applyLoopingParticles(player);
}
int World::canRest ()
@ -2831,6 +2832,19 @@ namespace MWWorld
mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection);
}
void World::applyLoopingParticles(const MWWorld::Ptr& ptr)
{
const MWWorld::Class &cls = ptr.getClass();
if (cls.isActor())
{
MWMechanics::ApplyLoopingParticlesVisitor visitor(ptr);
cls.getCreatureStats(ptr).getActiveSpells().visitEffectSources(visitor);
cls.getCreatureStats(ptr).getSpells().visitEffectSources(visitor);
if (cls.hasInventoryStore(ptr))
cls.getInventoryStore(ptr).visitEffectSources(visitor);
}
}
const std::vector<std::string>& World::getContentFiles() const
{
return mContentFiles;
@ -2847,11 +2861,17 @@ namespace MWWorld
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor);
}
bool World::isDark() const
bool World::useTorches() const
{
// If we are in exterior, check the weather manager.
// In interiors there are no precipitations and sun, so check the ambient
// Looks like pseudo-exteriors considered as interiors in this case
MWWorld::CellStore* cell = mPlayer->getPlayer().getCell();
if (cell->isExterior())
return mWeatherManager->isDark();
{
float hour = getTimeStamp().getHour();
return mWeatherManager->useTorches(hour);
}
else
{
uint32_t ambient = cell->getCell()->mAmbi.mAmbient;
@ -3012,13 +3032,17 @@ namespace MWWorld
void World::updateWeather(float duration, bool paused)
{
bool isExterior = isCellExterior() || isCellQuasiExterior();
if (mPlayer->wasTeleported())
{
mPlayer->setTeleported(false);
mWeatherManager->playerTeleported();
const std::string playerRegion = Misc::StringUtils::lowerCase(getPlayerPtr().getCell()->getCell()->mRegion);
mWeatherManager->playerTeleported(playerRegion, isExterior);
}
mWeatherManager->update(duration, paused);
const TimeStamp time = getTimeStamp();
mWeatherManager->update(duration, paused, time, isExterior);
}
struct AddDetectedReferenceVisitor

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

Loading…
Cancel
Save